Shader入门---曲面细分着色器和几何着色器

Shader入门—曲面细分着色器和几何着色器
前记:学不可以停止-------------------------------mx

基础知识:
曲面细分着色器:可以将一个几何体细化为一个球体也能将一根直线无限向曲线逼近

使用曲面细分的好处:直接使用顶点数更多的模型会带来更高的性能消耗,曲面细分能根据规则来动态调整模型的复杂度,达到对应的效果

曲面细分着色器的输入输出:

  1. 输入:Patch,可以看成是多个顶点的集合,包含每个顶点的属性,可以指定一个Patch包含的顶点数以及自己的属性
  2. 功能:将图元细分(可以是三角形、矩形等
  3. 输出:细分后的顶点

曲面细分着色器流程:

  1. Hull Shader : 可以编程,决定细分数量(设定Tessellation factor以及Inside Tessellation factor),对输入的Patch参数进行改变(如果需要)
  2. Tessellation Primitive Generation :无法编程,进行细分操作
  3. Domain Shader : 可以编程,对细分后的点进行处理,从重心空间(Barycentric coordi)转换到屏幕空间

Hull Shader各参数解析:
Shader入门---曲面细分着色器和几何着色器_第1张图片
几何着色器:专门处理场景里的几何图形,可以将创建或销毁几何图元,可以根据顶点的信息批量处理几何图形,对顶点附近的数据进行函数的处理,快速创造出新的多边形(Vertex Shader是专门处理多边形顶点的,而Geometry shader就是专门用来处理场景中的几何图形)

几何着色器的输入输出:

  1. 输入:图元—顶点,边,三角形等
    points-GL_POINTS (1)
    lines-GL_LINES, GL_LINE_STRIP, GL_LINE_LIST (2)
    lines_adjacency-GL_LINES_ADJACENCY, GL_LINE_STRIP_ADJACENCY (4)
    triangles-GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN(3)
    triangles_adjacency-GL_TRIANGLES_ADJACENCY, GL_TRIANGLE_STRIP_ADJACENCY (6)

  2. 输出:图元(输出图元类型跟输入图元类型完全不同,而且输出图元的数量和输入图元数量也没有关系)
    points
    line_strip
    triangle_strip

几何着色器的实际应用:

  1. 法线可视化
  2. 动态几何体形成
  3. 草地的生成

曲面细分着色器shader

Shader "Custom/TessShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _TessellationUniform ("_TessellationUniform", Range(1,64)) = 1

    }
    SubShader
    {
        Name "FORWARD"
        Tags 
        { 
        "LightMode"="ForwardBase"
        }
        LOD 200
        Pass{
        CGPROGRAM
        #pragma hull hullProgram
        #pragma domain ds
        #pragma vertex tessvert
        #pragma fragment frag
        #include "UnityCG.cginc"
        #include "Tessellation.cginc"
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 5.0

        sampler2D _MainTex;
        float4 _MainTex_ST;
        struct VertexInput
        {
            float4 vertex : POSITION;
            float3 normal : NORMAL ;
            float4 tangent : TANGENT ;
            float2 uv : TEXCOORD;
        };
        struct VertexOutput
        {
            float4 vertex : SV_POSITION;
            float3 normal :NORMAL;
            float4 tangent :TANGENT;
            float2 uv : TEXCOORD;
        };

        VertexOutput vert  (VertexInput v)//这个函数应用在domain函数中,用来处理坐标
        {
            VertexOutput o;
            o.uv =TRANSFORM_TEX(v.uv,_MainTex);
            o.vertex =UnityObjectToClipPos(v.vertex);
            o.normal =v.normal;
            o.tangent = v.tangent;
            return o;
        }
        #ifdef UNITY_CAN_COMPILE_TESSELLATION
            struct TessVertex
            {
                float4 vertex: INTERNALTESSPOS;
                float3 normal :NORMAL;
                float4 tangent :TANGENT;
                float2 uv :TEXCOORD0;
            };
            struct OutputPatchConstant//不同图元该结构会有所不同
            {
                float edge[3]  : SV_TESSFACTOR;
                float inside : SV_INSIDETESSFACTOR;
            };
            TessVertex tessvert (VertexInput v)
            {
                TessVertex o;
                o.vertex = v.vertex;
                o.normal = v.normal;
                o.tangent=v.tangent;
                o.uv = v.uv;
                return o;
            }
            float _TessellationUniform;
            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")]//去掉图元
            [UNITY_partitioning("fractional_odd")]
            [UNITY_outputtopology("triangle_cw")]
            [UNITY_patchconstantfunc("hsconst")]//规定曲面细分的属性,一个patch有三个点,但是三个点都会共用这个函数
            [UNITY_outputcontrolpoints(3)]//不同的图元会对应不同的控制点
            TessVertex hullProgram (InputPatch<TessVertex,3> patch ,uint id:SV_OutputControlPointID)
            {
                return patch[id];
            }
            [UNITY_domain("tri")]//同样需要定义图元
            VertexOutput ds (OutputPatchConstant tessFactors ,const OutputPatch<TessVertex,3> patch ,float3 bary :SV_DOMAINLOCATION)
            {
                VertexOutput 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.uv =patch[0].uv*bary.x + patch[1].uv *bary.y +patch[2].uv * bary.z;
                VertexOutput o =vert(v);
                return o;
            }
            #endif
            float4 frag(VertexOutput i) :SV_Target
            {
                return float4(1.0,1.0,1.0,1.0);
            }
            
         ENDCG
         }
    }
    FallBack "Diffuse"
}

Shader入门---曲面细分着色器和几何着色器_第2张图片

曲面细分着色器shader与置换贴图结合

Shader "Unlit/Tess_Diss_Shader"
{
    Properties
    {
        _MainTex("MainTex",2D) = "white"{}
        _DisplacementMap("_DisplacementMap",2D)="gray"{}
        _DisplacementStrength("DisplacementStrength",Range(0,1)) = 0
        _Smoothness("Smoothness",Range(0,5))=0.5
        _TessellationUniform("TessellationUniform",Range(1,64)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" 
               "LightMode"="ForwardBase"}
        LOD 100
        Pass
        {
            CGPROGRAM
            //定义2个函数 hull domain
            #pragma hull hullProgram
            #pragma domain ds
           
            #pragma vertex tessvert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            //引入曲面细分的头文件
            #include "Tessellation.cginc" 

            #pragma target 5.0
            float _TessellationUniform;
            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _DisplacementMap;
            float4 _DisplacementMap_ST;
            float _DisplacementStrength;
            float _Smoothness;

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

            struct VertexOutput
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
                float4 worldPos:TEXCOORD1;
                half3 tspace0 :TEXCOORD2;
                half3 tspace1 :TEXCOORD3;
                half3 tspace2 :TEXCOORD4;
            };

            VertexOutput vert (VertexInput v)
            //这个函数应用在domain函数中,用来空间转换的函数
            {
                VertexOutput o;
                o.uv = TRANSFORM_TEX(v.uv,_MainTex);
                //Displacement
                //由于并不是在Fragnent shader中读取图片,GPU无法获取mipmap信息,因此需要使用tex2Dlod来读取图片,使用第四坐标作为mipmap的level,这里取了0
                float Displacement = tex2Dlod(_DisplacementMap,float4(o.uv.xy,0.0,0.0)).g;
                Displacement = (Displacement-0.5)*_DisplacementStrength;
                v.normal = normalize(v.normal);
                v.vertex.xyz += v.normal * Displacement;

                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);

                //计算切线空间转换矩阵
                half3 vNormal = UnityObjectToWorldNormal(v.normal);
                half3 vTangent = UnityObjectToWorldDir(v.tangent.xyz);
                //compute bitangent from cross product of normal and tangent
                half tangentSign = v.tangent.w * unity_WorldTransformParams.w;
                half3 vBitangent = cross(vNormal,vTangent)*tangentSign;
                //output the tangent space matrix
                o.tspace0 = half3(vTangent.x,vBitangent.x,vNormal.x);
                o.tspace1 = half3(vTangent.y,vBitangent.y,vNormal.y);
                o.tspace2 = half3(vTangent.z,vBitangent.z,vNormal.z);
                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 (VertexInput v){
                    //顶点着色器函数
                    TessVertex o;
                    o.vertex  = v.vertex;
                    o.normal  = v.normal;
                    o.tangent = v.tangent;
                    o.uv      = v.uv;
                    return o;
                }

                //float _TessellationUniform;
                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 hullProgram (InputPatch<TessVertex,3> patch,uint id : SV_OutputControlPointID){
                    //定义hullshaderV函数
                    return patch[id];
                }

                [UNITY_domain("tri")]//同样需要定义图元
                VertexOutput ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3>patch,float3 bary :SV_DOMAINLOCATION)
                //bary:重心坐标
                {
                    VertexInput 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.uv = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z;

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

            float4 frag (VertexOutput i) : SV_Target
            {
                float3 lightDir =_WorldSpaceLightPos0.xyz;
                float3 tnormal = UnpackNormal (tex2D (_DisplacementMap, i.uv));
                half3 worldNormal;
                worldNormal.x=dot(i.tspace0,tnormal);
                worldNormal.y= dot (i.tspace1, tnormal);
                worldNormal.z=dot (i.tspace2, tnormal);
                float3 albedo=tex2D (_MainTex, i.uv). rgb;
                float3 lightColor = _LightColor0.rgb;
                float3 diffuse = albedo * lightColor * DotClamped(lightDir,worldNormal);
                float3 viewDir = normalize (_WorldSpaceCameraPos. xyz-i. worldPos. xyz);
                float3 halfVector = normalize(lightDir + viewDir);
                float3 specular = albedo * pow (DotClamped (halfVector, worldNormal), _Smoothness * 100);
                float3 result = specular + diffuse;
                return float4(result, 1.0);

                return float4(result,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

Shader入门---曲面细分着色器和几何着色器_第3张图片

几何着色器shader
_DissolveThreshold、_ColorFactor的值需要根据不同的模型修改

Shader "My/DoublePass"
{
 
Properties
{
		_Diffuse("Diffuse", Color) = (1,1,1,1)
		_DissolveColor("Dissolve Color", Color) = (0,0,0,0)
		_MainTex("Base 2D", 2D) = "white"{}
		_ColorFactor("ColorFactor", Range(0,0.001)) = 0.0004
		_DissolveThreshold("DissolveThreshold", Range(-0.002,0.02)) = 0  
		_BoxScale("BoxScale",Range(0,1)) = 0.001
		_BoxColor("BoxColor",Color) = (1,1,1,1)
		_Alpha("_Alpha",Range(0,1))=1
}
 
SubShader
{
Pass{
Tags{ "RenderType" = "Opaque" } 
	Cull Off
	CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag	
 
	#include "Lighting.cginc"
	uniform fixed4 _Diffuse;
	uniform fixed4 _DissolveColor;
	uniform sampler2D _MainTex;
	uniform float4 _MainTex_ST;
	uniform float _ColorFactor;
	uniform float _DissolveThreshold;  
	
	struct v2f
	{
		float4 pos : SV_POSITION;
		float3 worldNormal : TEXCOORD0;
		float2 uv : TEXCOORD1;
		float4 objPos : TEXCOORD2; 
	};
	
	v2f vert(appdata_base v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.vertex);
		o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
		o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
		o.objPos = v.vertex;  
		return o;
	}
	
	fixed4 frag(v2f i) : SV_Target
	{
		float factor = i.objPos.z -_DissolveThreshold ;
		clip(factor); 
		
		fixed3 worldNormal = normalize(i.worldNormal);
		fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
		fixed3 lambert = saturate(dot(worldNormal, worldLightDir));
		fixed3 albedo = lambert * _Diffuse.xyz * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz;
		fixed3 color = tex2D(_MainTex, i.uv).rgb * albedo;
 
		if (factor < _ColorFactor)
		{
			return _DissolveColor;
		}
		return fixed4(color, 1);
	}
	ENDCG
}
 

 
Pass {
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
	  Blend One One
      Cull Off
 
      CGPROGRAM
      #pragma target 4.0
      #pragma vertex vert
      #pragma geometry geo
      #pragma fragment frag
      #pragma multi_compile_fog
      #include "UnityCG.cginc"
 
			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float3 normal : NORMAL;
			};
 
			struct v2g
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float2 uv : TEXCOORD0;
				float4 objPos: TEXCOORD1;
			};
 
			struct g2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};
 
      sampler2D _MainTex;
	  float4 _MainTex_ST;
      float _BoxScale;
	  fixed4 _BoxColor;
	  float _ColorFactor;
	  float _DissolveThreshold; 
	  float _Alpha;
 
      v2g vert (appdata v) {
        v2g o;
        o.vertex = v.vertex;
        o.uv  = TRANSFORM_TEX(v.uv, _MainTex);
		o.objPos = v.vertex;
        return o;
      }
 
 
      #define ADD_VERT(v) \
        o.vertex = UnityObjectToClipPos(v); \
        TriStream.Append(o);
      
      #define ADD_TRI(p0, p1, p2) \
        ADD_VERT(p0) ADD_VERT(p1) \
        ADD_VERT(p2) \
        TriStream.RestartStrip();
 
      
      [maxvertexcount(36)]
      void geo(triangle v2g v[3], inout TriangleStream<g2f> TriStream) { 
 
        float4 vertex = (v[0].vertex + v[1].vertex + v[2].vertex) / 3;
        float2 uv = (v[0].uv + v[1].uv + v[2].uv) / 3;
 
		float3 edgeA = v[1].vertex - v[0].vertex;
		float3 edgeB = v[2].vertex - v[0].vertex;
		float3 normalFace = normalize(cross(edgeA, edgeB));
	 
 
		float factor =  vertex.z - _DissolveThreshold;
		//if(factor < 0) return;
		if(factor < _ColorFactor &&factor+_ColorFactor*2 > _ColorFactor)
		{
			//这里存粹是为效果而设置的,让它看上去是对的。

				vertex.xyz += normalFace * lerp(0,0.005,sin(_Time*0.5));
			//不同的消散效果可以调不同的参数

			g2f o;
			o.uv = uv;
			float scale = _BoxScale;
 
			float4 v0 = float4( 1, 1, 1,1)*scale + float4(vertex.xyz,0);
			float4 v1 = float4( 1, 1,-1,1)*scale + float4(vertex.xyz,0);
			float4 v2 = float4( 1,-1, 1,1)*scale + float4(vertex.xyz,0);
			float4 v3 = float4( 1,-1,-1,1)*scale + float4(vertex.xyz,0);
			float4 v4 = float4(-1, 1, 1,1)*scale + float4(vertex.xyz,0);
			float4 v5 = float4(-1, 1,-1,1)*scale + float4(vertex.xyz,0);
			float4 v6 = float4(-1,-1, 1,1)*scale + float4(vertex.xyz,0);
			float4 v7 = float4(-1,-1,-1,1)*scale + float4(vertex.xyz,0);
 
 
			ADD_TRI(v0, v2, v3);
			ADD_TRI(v3, v1, v0);
			ADD_TRI(v5, v7, v6);
			ADD_TRI(v6, v4, v5);
 
			ADD_TRI(v4, v0, v1);
			ADD_TRI(v1, v5, v4);
			ADD_TRI(v7, v3, v2);
			ADD_TRI(v2, v6, v7);
 
			ADD_TRI(v6, v2, v0);
			ADD_TRI(v0, v4, v6);
			ADD_TRI(v5, v1, v3);
			ADD_TRI(v3, v7, v5);
        }	  
		else
		{
			vertex.xyz =float3(0,0,0);
		}
      }
 
      fixed4 frag (g2f i) : SV_Target {
        float4 col = _BoxColor;
		col.a = _Alpha;
        return col;
      }
      ENDCG
}
    }
 }

Shader入门---曲面细分着色器和几何着色器_第4张图片

学习相关链接:
https://www.bilibili.com/video/BV1XX4y1A7Ns
https://blog.csdn.net/qq_37925032/article/details/82936769
https://www.yuque.com/docs/share/4941142e-6c9e-4f6d-8e53-ee053ef42a2e?#
https://www.yuque.com/sugelameiyoudi-jadcc/okgm7e/xyx5h5
https://zhuanlan.zhihu.com/p/76775024
https://blog.csdn.net/vampirem/article/details/12152945?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%87%A0%E4%BD%95%E7%9D%80%E8%89%B2%E5%99%A8&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-7-.nonecase&spm=1018.2226.3001.4187

------------------------------博主:mx

你可能感兴趣的:(Shader学习笔记,unity3d,渲染器,3d渲染,opengl,directx)