Unity shader - 如何根据纹理贴图生成法线贴图

Unity shader - How to generate normal map based on texture map

我目前卡在我正在编写的着色器中。 我正在尝试创建一个雨水着色器。 我已经设置了 3 个模拟雨的粒子系统,还有一个 相机看这个模拟。相机视图是我 用作纹理。在我的着色器中,我现在正在尝试制作法线贴图 来自那个纹理贴图,但我不知道该怎么做。

Shader "Unlit/Rain"
{
    Properties
    {
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
        _NormalIntensity("NormalIntensity",Float) = 1
      
    }
        SubShader
        {
            
            Tags { "RenderType" = "Opaque" }
            LOD 100

            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"
                #include "Lighting.cginc"
                #include "AutoLight.cginc"

                struct VertexInput {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                    float4 normal : NORMAL;
                    float3 tangent : TANGENT;

                };

                struct VertexOutput {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                    float2 uv1 : TEXCOORD1;
                    float4 normals : NORMAL;

                    float3 tangentSpaceLight: TANGENT;
                   
                };

                sampler2D _MainTex;
                float4 _MainTex_ST;
                half4 _Color;
                float _NormalIntensity;

                VertexOutput vert(VertexInput v) {

                    VertexOutput o;
                    o.normals = v.normal;
                    o.uv1 = v.uv;
                    o.vertex = UnityObjectToClipPos( v.vertex );
                   // o.uv = TRANSFORM_TEX( v.uv, _MainTex ); // used for texture

                    return o;
                }



                float4 frag(VertexOutput i) : COLOR{

                    float4 col2 = tex2D(_MainTex, i.uv1); 
                    return col2 * i.normals * 5;
                }
                ENDCG
            }
        }
}

This is what the camera sees. I set the TargetTexture for this camera to be a texture I created.

In my shader I then put that texture as an albedo property

所以我现在想做的是找到该纹理的法线来创建凹凸贴图。

看起来您的“TargetTexture”正在返回一个高度图。这是我发现的关于 how to turn a height map into a normal map 的 post。我已经将你最初的代码与那个论坛的核心混在一起 post 并将法线输出为颜色,这样你就可以测试并看看它是如何工作的:

Shader "Unlit/HeightToNormal"
{
    Properties
    {
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
        _NormalIntensity("NormalIntensity",Float) = 1
        _HeightMapSizeX("HeightMapSizeX",Float) = 1024
        _HeightMapSizeY("HeightMapSizeY",Float) = 1024
    }
    SubShader
    {

        Tags { "RenderType" = "Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float4 normal : NORMAL;
                float3 tangent : TANGENT;

            };

            struct VertexOutput {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float2 uv1 : TEXCOORD1;
                float4 normals : NORMAL;

                //float3 tangentSpaceLight: TANGENT;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            half4 _Color;
            float _NormalIntensity;
            float _HeightMapSizeX;
            float _HeightMapSizeY;

            VertexOutput vert(VertexInput v) {

                VertexOutput o;
                o.uv = TRANSFORM_TEX( v.uv, _MainTex ); // used for texture
                o.uv1 = v.uv;
                o.normals = v.normal;
                o.vertex = UnityObjectToClipPos(v.vertex);

                 return o;
             }

            float4 frag(VertexOutput i) : COLOR
            {
                float me = tex2D(_MainTex,i.uv1).x;
                float n = tex2D(_MainTex,float2(i.uv1.x, i.uv1.y + 1.0 / _HeightMapSizeY)).x;
                float s = tex2D(_MainTex,float2(i.uv1.x, i.uv1.y - 1.0 / _HeightMapSizeY)).x;
                float e = tex2D(_MainTex,float2(i.uv1.x + 1.0 / _HeightMapSizeX,i.uv1.y)).x;
                float w = tex2D(_MainTex,float2(i.uv1.x - 1.0 / _HeightMapSizeX,i.uv1.y)).x;

                // defining starting normal as color has some interesting effects, generally makes this more flexible
                float3 norm = _Color;
                float3 temp = norm; //a temporary vector that is not parallel to norm
                if (norm.x == 1)
                temp.y += 0.5;
                else
                temp.x += 0.5;

                //form a basis with norm being one of the axes:
                float3 perp1 = normalize(cross(i.normals,temp));
                float3 perp2 = normalize(cross(i.normals,perp1));

                //use the basis to move the normal i its own space by the offset
                float3 normalOffset = -_NormalIntensity * (((n - me) - (s - me)) * perp1 + ((e - me) - (w - me)) * perp2);
                norm += normalOffset;
                norm = normalize(norm);

                // it's also interesting to output temp, perp1, and perp1, or combinations of the float samples.
                return float4(norm, 1);
            }
            ENDCG
         }
    }
}

要从高度贴图生成法线贴图,您要尝试使用高度贴图中的定向变化率来得出一个法向量,该向量可以使用 3 个浮点值(或颜色通道,如果它是图像)。您可以对您所在的图像点进行采样,然后在四个主要方向上远离该点一小步。使用叉积来保证正交性,您可以定义一个基础。使用图像上的定向步骤,您可以缩放两个基本向量并将它们加在一起以找到“法线偏移”,这是近似高度图上值的定向变化的 3D 表示。基本上是你的常态。

你可以看到我正常强度打球的效果here, and the "normal color" here。当这看起来适合您的用例时,您可以尝试使用法线作为法线而不是彩色输出。

可能仍需要对值进行一些调整。祝你好运!