普通的Shader_草地效果

学习草地效果的shader制作。参考文章-草地shader

思路:

通过几何着色器,使用网格顶点生成面片来模拟草,使用曲面细分着色器改变顶点量来控制草密度。
添加阴影的投射和接收,光的接收。
给每个面片草添加随机属性。
使用流动纹理模拟风吹效果。

添加交互效果。(未完成)

知识点:

  1. 几何着色器的使用
  2. 曲面细分着色器的使用
  3. 在使用几何着色器后,阴影的使用方式(与顶点片元着色器的使用区别)
  4. 切线空间TBN的相关知识和计算
  5. 旋转矩阵的构造

代码:

跟着教程写的代码存在几个问题:

  1. 草自身接收阴影的问题 添加宏之后并没有效果,还存在横线
  2. 法线朝向貌似存在问题
  3. 两个pass的tags标签定义
    shader:
Shader "Unlit/GrassGeometryShader"
{
    
    
//草效果
//1.模型每个顶点生成三角面片(面片的坐标需要在切线空间TBN矩阵构造)_几何着色器
//2.加颜色
//3.加随机变量(朝向(z轴),弯曲(x轴),尺寸)_矩阵构造
//4.曲面细分控制密度(动态细分性能优化)_曲面细分着色器
//5.草晃动(扭曲纹理,草底部顶点不动,顶部移动)_流动纹理采样
//6.单个草细致化(将草由一个三角面细化为多个三角面组成,使其可弯曲)
//7.对分段后的草添加弯曲
//8.投射阴影(增加投射阴影的pass)
//9.接收阴影(和正常阴影三剑客区别,结构体属性unityShadowCoord4,)
//10.接收光照
    Properties
    {
    
    
        _MainTex ("Texture", 2D) = "white" {
    
    }
        _GrassTopColor("Top Color",COLOR) = (1,1,1,1)
        _GrassBottomColor("Botton Color",COLOR) = (1,1,1,1)
        _BendDegree("Grass Bend Degree",Range(0,1)) = 1
        _GrassWidth("Grass Width",Range(0,1))=0.2
        _GrassHeight("Grass Height",Range(0,2)) = 1
        _TessellationUniform("TessellationUniform",Range(1,64)) = 1
        //流动纹理
        _FlowMap("Flow Map",2D)="black"{
    
    }
        _WindScale("Wind Scale",Range(0,0.1))=0.01
        _WindStrength("Wind Strength",Range(0,5))=1
        //弯曲
        _BladeForward("Blade Forward Amount", Range(-1,1)) = 0.38
        _BladeCurve("Blade Curvature Amount", Range(1, 4)) = 2
    }
    SubShader
    {
    
    

        CGINCLUDE
            #include "UnityCG.cginc"
            //#include "Lighting.cginc"
            #include "Autolight.cginc"
            #define PI 3.1415926
            //草叶的段数
            #define BLADE_SEGMENTS 3

            struct v2g
            {
    
    
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct g2f{
    
    
                float4 vertex:SV_POSITION;
                float2 uv:TEXCOORD0;
                unityShadowCoord4 _ShadowCoord : TEXCOORD1;
                float3 normal:NORMAL;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _GrassTopColor;
            fixed4 _GrassBottomColor;
            float _BendDegree;
            float _GrassWidth;
            float _GrassHeight;
            float _TessellationUniform;

            sampler2D _FlowMap;
            float _WindStrength;
            float _WindScale;
            
            float _BladeCurve;
            float _BladeForward;

            g2f CreateTriangle(float3 pos,float2 uv,float3 normal){
    
    
                g2f o;
                o.vertex = UnityObjectToClipPos(pos);
                o.uv = uv;
                o._ShadowCoord = ComputeScreenPos(o.vertex);
                o.normal = UnityObjectToWorldNormal(normal);
                #if UNITY_PASS_SHADOWCASTER
                    o.vertex = UnityApplyLinearShadowBias(o.vertex);
                #endif 
                return o;
            }

            float rand(float3 z){
    
    
                return frac(sin(dot(z.xyz,float3(12.9898,78.233,53.539))) * 43758.5453);
            }

            float3x3 RotMatrixByAxisAngle(float angle,float3 axis){
    
    
                float c,s;
                sincos(angle,s,c);

                float t = 1-c;
                float x = axis.x;
                float y = axis.y;
                float z = axis.z;

                return float3x3(
                    t*x*x +c,   t*x*y - s*z,    t*x*z + s*y,
                    t*x*y + s*z,    t*y*y +c,   t*y*z - s*x,
                    t*x*z - s*y,    t*y*z +s*x,    t*z*z+c
                );
            }


            
            v2g vert (appdata_tan v)
            {
    
    
                v2g o;
                o.vertex = v.vertex;
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normal = v.normal;
                o.tangent = v.tangent;
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

           ///曲面细分部分
           
           
           //有些硬件不支持曲面细分着色器,定义了该宏就能够在不支持的硬件上不会变粉,也不会报错
            #ifdef UNITY_CAN_COMPILE_TESSELLATION
                //顶点着色器结构的定义
                struct TessVertex{
    
    
                    float4 vertex : INTERNALTESSPOS;
                    float3 normal : NORMAL;
                    float4 tangent : TANGENT;
                    float2 uv : TEXCOORD0;
                };

                struct OutputPatchConstant {
    
     
                    //不同的图元,该结构会有所不同
                    //该部分用于Hull Shader里面
                    //定义了patch的属性
                    //Tessellation Factor和Inner Tessellation Factor
                    float edge[3] : SV_TESSFACTOR;
                    float inside  : SV_INSIDETESSFACTOR;
                };

                TessVertex tessvert (appdata_tan v){
    
    
                    //顶点着色器函数
                    TessVertex o;
                    o.vertex  = v.vertex;
                    o.normal  = v.normal;
                    o.tangent = v.tangent;
                    o.uv      = v.texcoord;
                    return o;
                }

            
                OutputPatchConstant hsconst (InputPatch<TessVertex,3> patch){
    
    
                    //定义曲面细分的参数
                    OutputPatchConstant o;
                    o.edge[0] = _TessellationUniform;
                    o.edge[1] = _TessellationUniform;
                    o.edge[2] = _TessellationUniform;
                    o.inside  = _TessellationUniform;
                    return o;
                }

                [UNITY_domain("tri")]//确定图元,quad,triangle等
                [UNITY_partitioning("fractional_odd")]//拆分edge的规则,equal_spacing,fractional_odd,fractional_even
                [UNITY_outputtopology("triangle_cw")]
                [UNITY_patchconstantfunc("hsconst")]//一个patch一共有三个点,但是这三个点都共用这个函数
                [UNITY_outputcontrolpoints(3)]      //不同的图元会对应不同的控制点
              
                TessVertex hull (InputPatch<TessVertex,3> patch,uint id : SV_OutputControlPointID){
    
    
                    //定义hullshaderV函数
                    return patch[id];
                }

                [UNITY_domain("tri")]//同样需要定义图元
                v2g ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3>patch,float3 bary :SV_DOMAINLOCATION)
                //bary:重心坐标
                {
    
    
                    appdata_tan v;
                    v.vertex = patch[0].vertex*bary.x + patch[1].vertex*bary.y + patch[2].vertex*bary.z;
			        v.tangent = patch[0].tangent*bary.x + patch[1].tangent*bary.y + patch[2].tangent*bary.z;
			        v.normal = patch[0].normal*bary.x + patch[1].normal*bary.y + patch[2].normal*bary.z;
			        v.texcoord.xy = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z;

                    v2g o = vert (v);
                    return o;
                }
            #endif


           ///



            //生成三角面片
            [maxvertexcount(BLADE_SEGMENTS*2 +1 )]
            void geom(point v2g IN[1],inout TriangleStream<g2f> triStream){
    
    
                
                
                float4 centerPos = IN[0].vertex;
                float2 uv = IN[0].uv;
                float3 pos;

                //构造切线空间
                float3 binormal = cross(IN[0].normal,IN[0].tangent) * IN[0].tangent.w;
                float3x3 TBN = float3x3(
                    IN[0].tangent.x,binormal.x,IN[0].normal.x,
                    IN[0].tangent.y,binormal.y,IN[0].normal.y,
                    IN[0].tangent.z,binormal.z,IN[0].normal.z
                );

               
                //获取随机数
                float randangle = rand(centerPos) * PI;
                //z轴旋转矩阵和切线空间合并(朝向)(切线空间内z轴是朝上的)
                float3x3 finalMatrix = mul (TBN , RotMatrixByAxisAngle(randangle,float3(0,0,1)));//进行一次旋转

                //x轴旋转(草的弯曲程度)
                randangle = rand(centerPos.xzy) * _BendDegree * PI * 0.5;//0-90度内弯曲,_BendDegree弯曲程度
                float3x3 bendMatrix = RotMatrixByAxisAngle(randangle,float3(1,0,0));
                finalMatrix = mul(finalMatrix,bendMatrix);
               

                风场效果
                //构造uv坐标
                float2 moveUV = centerPos.xz * _Time.y *_WindScale;
                //Wind采样
		        float2 windSample = (tex2Dlod(_FlowMap, float4(moveUV, 0, 0)).xy * 2 - 1) * _WindStrength;
		        float3 wind = normalize(float3(windSample.x, windSample.y, 0));//Wind Vector
		        //Wind旋转矩阵
		        float3x3 windRotation = RotMatrixByAxisAngle(UNITY_PI * windSample, wind);
                float3x3 finalMatrixWind = mul(finalMatrix,windRotation);

                

                 //尺寸
                float wid = _GrassWidth + _GrassWidth * rand(centerPos.xxz);
                float hei = _GrassHeight + _GrassHeight * rand(centerPos.zzy);
                float forward = rand(centerPos.zyy)*_BladeForward;//弯曲
                //法线
                float3 normaldir = float3(0,-1,forward);
                float3 normaldirTBN = mul(TBN,normaldir);
                //草叶分段后的尺寸计算(注意顺序,必须按照顺序来写,从底到高,否则虽然看起来一致,带面片是错的。)
                //最底部的两个顶点 (不需要弯曲)
                pos = mul(finalMatrix,float3(-wid,0,0));
                triStream.Append(CreateTriangle(centerPos + pos,float2(0,0),normaldirTBN));

                pos = mul(finalMatrix,float3(wid,0,0));
                triStream.Append(CreateTriangle(centerPos + pos,float2(1,0),normaldirTBN));
                //中间顶点
                for (int i = 1; i < BLADE_SEGMENTS; i++)
                {
    
    
	                float t = i / (float)BLADE_SEGMENTS;
                    float segmentHeight = hei * t;
	                float segmentWidth = wid * (1 - t);
                    float segmentForward = pow(t,_BladeCurve)*forward;
                    pos = mul(finalMatrixWind,float3(-segmentWidth,segmentForward,segmentHeight));
                    triStream.Append(CreateTriangle(centerPos + pos,float2(0,t),normaldirTBN));
                    pos = mul(finalMatrixWind,float3(segmentWidth,segmentForward,segmentHeight));
                    triStream.Append(CreateTriangle(centerPos + pos,float2(1,t),normaldirTBN));
                }
                //最顶部
                pos = mul(finalMatrixWind,float3(0,forward,hei));
                triStream.Append(CreateTriangle(centerPos + pos,float2(0.5,1),normaldirTBN));

            }









        ENDCG

        Pass
        {
    
      
            Tags{
    
       
                "LightMode"="ForwardBase"
                "RenderType"="Opaque"
            }
            Cull Off
            CGPROGRAM

            #pragma vertex tessvert
            #pragma geometry geom
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #pragma hull hull
            #pragma domain ds
            
            #pragma multi_compile_fwdbase
            #include "Lighting.cginc"

            //fixed4 frag (g2f i) : SV_Target
            //{
    
    
            //    float shadowatt = SHADOW_ATTENUATION(i);
            //    // sample the texture
            //    fixed4 col = tex2D(_MainTex, i.uv);
            //    // apply fog
            //    UNITY_APPLY_FOG(i.fogCoord, col);

            //    col = lerp(_GrassTopColor,_GrassBottomColor,1-i.uv.y)*shadowatt;
            //    return col;
            //}

            fixed4 frag(g2f i,fixed facing :VFACE):SV_Target{
    
    
                //float3 normal = facing >0 ?i.normal:-i.normal;
                //return float4(normal*0.5+0.5,1);
                // 判断内外表面
	            float3 normal = facing > 0 ? i.normal : -i.normal;
	            // 获取阴影
	            float shadow = SHADOW_ATTENUATION(i);
	            //半罗伯特反射
	            float diffuse = (1 + dot(_WorldSpaceLightPos0, normal) )/ 2 * shadow;
	            //详看链接文章
	            float3 ambient = ShadeSH9(float4(normal, 1));
	            float4 lightIntensity = diffuse * _LightColor0 +float4(ambient, 1);
	            float4 col = lerp(_GrassBottomColor, _GrassTopColor * lightIntensity, i.uv.y);
	            return col;

                
                //return SHADOW_ATTENUATION(i);
            }
            ENDCG
        }

        //投射阴影pass
        Pass{
    
    
            Tags{
    
       
                "LightMode"="ShadowCaster"
            }
        
            CGPROGRAM
            #pragma vertex tessvert
            #pragma geometry geom
            #pragma fragment frag
            // make fog work
            #pragma hull hull
            #pragma domain ds

           // #include "Lighting.cginc"
            #pragma multi_compile_shadowcaster

            float4 frag(g2f i):SV_Target{
    
    
                SHADOW_CASTER_FRAGMENT(i);
            }

            ENDCG
        }
    }
}

猜你喜欢

转载自blog.csdn.net/suixinger_lmh/article/details/125147992