Unity URP PBR_Cook-Torrance模型

news2024/9/24 17:12:47

 Cook-Torrance模型是一个微表面光照模型,认为物体的表面可以看作是由许多个理想的镜面反射体微小平面组成的。

单点反射=镜面反射+漫反射占比*漫反射

漫反射 = 基础色/Π

镜面反射=DFG/4(N·V)(N·L)

D代表微平面分布函数,描述的是法线与半角向量normalize(L+V)对齐的概率,对齐程度越高,反射程度越大。

F代表菲涅尔反射函数,描述的是视线与法线的夹角越小时,向镜面反射集中,反之向漫反射集中。

G代表几何衰减系数,源自微表面的自我遮蔽现象,入射光线或反射光线会被凹凸表面遮挡,越粗糙的材质表面越可能发生自我遮蔽

但是想要计算出微表面的光照,单点反射当然是不够的,对于一个点,有多个角度会产生光照结果,所以对于光照输入角度的积分是必须的。

输出光={单点反射*输入光*(N·V) }对于输入角度的积分

对于直接光的计算,通常可以直接遍历所有光源计算即可,但对于漫反射的计算,通常会采用近似的方式,因为运行时的积分计算是十分昂贵的。所以通常会使用近似函数或预计算采样的方式实现。

想要了解更多关于Cook-Torrance模型可以去知乎检索。

下面是具体实现代码:

Shader "Kerzh/PBRhlsl"
{
    Properties
    {
    	_TessellationUniform ("Tessellation Uniform", Vector) = (1,1,1,1)  //  曲面细分系数,默认1代表不做操作
        
        [Space(10)]
        _BaseColorTex ("_BaseColorTex", 2D) = "white" {}
        _MetallicTex ("_MetallicTex", 2D) = "white" {}
        _RoughnessTex ("_RoughnessTex", 2D) = "white" {}
        _EmissionTex ("_EmissionTex", 2D) = "black" {}//默认传入黑色,即不存在自发光
        
        [Space(10)]
        _NormalTex ("_NormalTex", 2D) = "bump" {}//默认传入是(0.5,0.5,1),这是法线的标准颜色,相当于一张没有影响的法线图
        _AOTex ("_AOTex", 2D) = "white" {}  // 默认传入白色,即不存在光线遮蔽
        
        [Space(10)]
        [Toggle(ENABLE_BRDF_LUT)]_EnableBrdfLut("Enable BrdfLut",Float) = 0
        _BRDFLut("BRDFLut",2D) = "black"{}
    }
    SubShader
    {
        Tags { "RenderPipeline" = "UniversalRenderPipeline" "LightMode" = "UniversalForward" }
        
        Pass
        {
            HLSLPROGRAM
		    #pragma vertex vert
			#pragma hull hull
			#pragma domain domain
		    #pragma geometry geom
			#pragma fragment frag
			#pragma target 4.6
		    
            #pragma shader_feature _ ENABLE_BRDF_LUT  // _ 默认关闭,是否使用预计算漫反射第二部分
		    #pragma multi_compile _ADDITIONAL_LIGHTS_VERTEX  //  用于激活VertexLighting方法中对次要光源的计算

            #include "CommonHlslInc.hlsl"
			#include "CustomTessellation.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SphericalHarmonics.hlsl"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Value, _RangeValue;
            float4 _Color, _BaseColor;
            float _Metallic, _Roughness;
            sampler2D _BaseColorTex, _MetallicTex, _RoughnessTex;
            sampler2D _EmissionTex, _AOTex, _NormalTex;
            sampler2D _BRDFLut;
            float4 _Emission;
		    
            #define Eplison 0.001  //  防曝光点
            
            //D  微平面分布函数  Trowbridge-Reitz模型
            float D_DistributionGGX(float3 N, float3 H, float Roughness)
            {
                float a = Roughness * Roughness;
                float a2 = a * a;
                float NH = max(dot(N, H), 0);
                float NH2 = NH * NH;
                float nominator = a2;
                float denominator = (NH2 * (a2 - 1.0) + 1.0);
                denominator = PI * denominator * denominator;

                return nominator / max(denominator, Eplison); //防止分母为0
            }

            //G  几何衰减系数  Schlick-GGX模型
            float GeometrySchlickGGX(float NV, float Roughness)
            {
                float r = Roughness + 1.0;
                float k = r * r / 8.0;
                float nominator = NV;
                float denominator = k + (1.0 - k) * NV;
                return nominator / max(denominator, Eplison); //防止分母为0
            }

            //  获取几何衰减系数
            float G_GeometrySmith(float3 N, float3 V, float Roughness)
            {
                float NV = max(dot(N, V), 0);
                float ggx = GeometrySchlickGGX(NV, Roughness);
                return ggx;
            }

            //F ->直接光
            float3 F_FrenelSchlick(float NV, float3 F0)
            {
                return F0 + (1.0 - F0) * pow(1.0 - NV, 5);
            }
            
            //F ->间接光
            float3 FresnelSchlickRoughness(float NV, float3 F0, float Roughness)
            {
                float s = 1.0 - Roughness;
                return F0 + (max(s.xxx, F0) - F0) * pow(1.0 - NV, 5.0);
            }
            
            //UE4中的近似计算函数 用于近似计算间接光漫反射第二部分
            float2 EnvBRDFApprox(float Roughness, float NoV)
            {
				const half4 c0 = { -1, -0.0275, -0.572, 0.022 };
				const half4 c1 = { 1, 0.0425, 1.04, -0.04 };
				half4 r = Roughness * c0 + c1;
				half a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
				half2 AB = half2(-1.04, 1.04) * a004 + r.zw;
				return AB;
            }

            MeshData vert(MeshData input)
            {
                return input;
            }

            //如果不正确配置会报错
			[domain("tri")]  //  正在处理三角形   "tri", "quad", or "isoline"
			[outputcontrolpoints(3)]  //  每个面片输出的顶点为3个
			[outputtopology("triangle_cw")]  //  当创建三角形时应是顺时针还是逆时针,这里应是顺时针  "point", "line", "triangle_cw", or "triangle_ccw"
			[partitioning("integer")]  //  如何细分切割面片   "integer", "pow2", "fractional_even", or "fractional_odd"
			[patchconstantfunc("patchConstantFunction")]  //  细分切割部分还必须提供函数处理,每个面片调用一次
			MeshData hull (InputPatch<MeshData, 3> patch, uint id : SV_OutputControlPointID)  //  每个顶点调用一次,如果是处理三角形就是调用三次
			{
				//  如果_TessellationUniform输入值为1,不产生任何变化,但当输入值为2时,具体发生了什么
				//  对于一个三角形(因为我们配置的是处理三角形),根据三条边的二等分位置添加额外的一个顶点,如果是3就是三等分位置添加两个顶点
				//  对于一个三角形(因为我们配置的是处理三角形),根据三条个顶点添加一个中心顶点
				//  根据patchConstantFunction赋值分配生成顶点的插值权重
				return patch[id];
			}

			//  barycentricCoordinates分别代表各个点的插值权重,每个面片调用一次,对细分后的三角顶点形进行处理(也就是说原顶点不经过此处理?)
			[domain("tri")]  //  正在处理三角形
			MeshData domain(TessellationFactors factors, OutputPatch<MeshData, 3> patch, float3 barycentricCoordinates : SV_DomainLocation)  
			{
				MeshData data;  //  新的插值权重顶点
				
				#define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) data.fieldName = \
				patch[0].fieldName * barycentricCoordinates.x + \
				patch[1].fieldName * barycentricCoordinates.y + \
				patch[2].fieldName * barycentricCoordinates.z;

				//对所有信息利用插值权重进行插值计算
				MY_DOMAIN_PROGRAM_INTERPOLATE(vertex)
				MY_DOMAIN_PROGRAM_INTERPOLATE(normalTS)
				MY_DOMAIN_PROGRAM_INTERPOLATE(tangentTS)
				MY_DOMAIN_PROGRAM_INTERPOLATE(uv1)
				MY_DOMAIN_PROGRAM_INTERPOLATE(uv2)
				MY_DOMAIN_PROGRAM_INTERPOLATE(vertexColor)
				return data;
			}

		    //最大的顶点数,这里比如你要生成三个三角形面片,那么一个面片需要三个顶点,就是9个顶点,一般来讲这里直接填多一点即可,不过可能填太多了会导致内存占用?
            [maxvertexcount(9)]
            void geom (triangle MeshData input[3],inout TriangleStream<VOutData> triStream)
            {
            	//原样转换过去
            	VOutData output[3];
            	for(int i=0;i<3;i++)
                {
                    VOutData p0;
                    p0 = FillBaseV2FData(input[i]);
                    output[i] = p0;
                }

				triStream.RestartStrip(); //  重新开始一个新的三角形
				triStream.Append(output[0]);
                triStream.Append(output[1]);
                triStream.Append(output[2]);
            }

            float4 frag(VOutData i) : SV_Target
            {
                float3 lightDirWS = normalize(GetMainLight().direction);
                float3 viewDirWS = normalize(GetWorldSpaceViewDir(i.posWS));
                float3 halfVectorWS = normalize(viewDirWS + lightDirWS);
                float2 uv = i.uv1;

                //================== Normal Map  ============================================== //
                //这里我们采样到一个法线贴图,向量是在切线空间下的,我希望把他变换到世界空间,所以我需要创建一个切线到世界的变换矩阵
                float3 NormalMap = UnpackNormal(tex2D(_NormalTex, uv));
                //在创造一个转换矩阵时,使用新坐标基在原空间中的向量构建,这样的矩阵可以从原坐标系变换到新坐标系
                //所以这里想要构建一个从切线空间转换到到世界空间的矩阵,虽然我们没有切线空间下的世界空间坐标,但至少拥有世界空间下的切线坐标基
                //且世界空间下这三个坐标基,正好是两两垂直的,满足正交矩阵,可以使用一个重要的性质:矩阵的逆=矩阵的转置
                //而这个矩阵的逆就是反向变换,即我们最开始想要的切线空间到世界空间的变换。
                float3x3 TangentToWorldMatrix = transpose(float3x3(i.tangentWS, normalize(i.bitangentWS), i.normalWS));
                float3 normalWS = normalize(mul(TangentToWorldMatrix, NormalMap));
                //================== PBR 贴图采样 ============================================== //
                float3 BaseColor = tex2D(_BaseColorTex, uv);
                float Roughness = tex2D(_RoughnessTex, uv).r;
                float Metallic = tex2D(_MetallicTex, uv).r;
                float3 Emission = tex2D(_EmissionTex, uv);
                float3 AO = tex2D(_AOTex, uv);
                //================== Direct Light 直接光部分 ============================================== //
                //Cook-Torrance BRDF模型
                float HV_WS = saturate(dot(halfVectorWS, viewDirWS));
                float NV_WS = saturate(dot(normalWS, viewDirWS));
                float NL_WS = saturate(dot(normalWS, lightDirWS));
                float NH_WS = saturate(dot(normalWS, halfVectorWS));

                //首先计算PBR三件套的DFG  要特别注意在这个PBR模型中直接光和间接光的F不同
                //微表面分布函数,描述的法线与半角向量对齐的概率,概率越大则反射越强
                float D = D_DistributionGGX(normalWS, halfVectorWS, Roughness);
                //菲尼尔,多影响高光和漫反射混合比例
            	float3 F0 = lerp(0.04, BaseColor, Metallic);//经验近似值
                float3 F = F_FrenelSchlick(NV_WS, F0);
                //几何衰减系数,源自微表面的自我遮蔽现象,入射光线或反射光线会被凹凸表面遮挡,越粗糙的材质表面越可能发生自我遮蔽
                float G = G_GeometrySmith(normalWS, viewDirWS, Roughness);

                //还记得公式怎么写的吗
                //单点反射=镜面反射占比*镜面反射+漫反射占比*漫反射
                //漫反射 = 基础色/Π
                //镜面反射=DFG/4(N·V)(N·L)
            	//输出光={单点反射*输入光*(N·V)}对于输入角度的积分
				//------------------------------直接光-----漫反射------------------------------
            	float3 Diffuse_Direct;
            	//直接光漫反射--光源
            	float3 Diffuse_Direct_lightSource = BaseColor / PI;

            	//直接光漫反射--比例:用(1-F)(1-metallc)公式
                float3 KD_DirectLight;
            	KD_DirectLight = float3(1, 1, 1) - F;
                KD_DirectLight *= 1 - Metallic;
            	
            	Diffuse_Direct = Diffuse_Direct_lightSource * KD_DirectLight;
            	//------------------------------直接光-----漫反射------------------------------
            	//------------------------------直接光-----高光------------------------------
				float3 Specular_Direct;
            	Specular_Direct = (D * F * G)/max(4*NV_WS*NL_WS, Eplison);
				//------------------------------直接光-----高光------------------------------
            	//获得输入光
                float3 lightInput = VertexLighting(i.posWS, i.normalWS) + GetMainLight().color;  //  包含Mainlight和AdditionalLights的输入光
                //而对于直接光,不用计算积分,因为只有一个直接光。使用这个公式:输出光={单点反射*输入光*(N·V)}对于输入角度的积分
                float3 DirectLightFinal = (Diffuse_Direct + Specular_Direct)* NL_WS * lightInput;
                //================== Indirect Light 间接光部分 ============================================== //
            	//------------------------------间接光-----漫反射------------------------------
            	float3 Diffuse_Indirect;
            	
                //间接光漫反射--比例:用(1-F)(1-metallc)公式
                float3 KD_IndirectLight;
            	//间接光 菲尼尔
                float3 F_IndirectLight = FresnelSchlickRoughness(NV_WS, F0, Roughness);
            	KD_IndirectLight= float3(1, 1, 1) - F_IndirectLight;
                KD_IndirectLight *= 1 - Metallic;

            	//间接光漫反射--辐照度:用球谐函数计算辐照度,在urp下不需要额外设置光照探针烘焙
            	float3 irradianceSH;
            	//球谐基数
            	real4 SHCoefficients[7];  //  https://blog.csdn.net/qq_41835314/article/details/129991046
			    SHCoefficients[0] = unity_SHAr;
			    SHCoefficients[1] = unity_SHAg;
			    SHCoefficients[2] = unity_SHAb;
			    SHCoefficients[3] = unity_SHBr;
			    SHCoefficients[4] = unity_SHBg;
			    SHCoefficients[5] = unity_SHBb;
			    SHCoefficients[6] = unity_SHC;
            	irradianceSH = SampleSH9(SHCoefficients, normalWS);  //  计算球谐辐照度

            	//间接光漫反射--光源:使用公式,辐照度*BaseColor/PI 这里乘上辐照度相当于单点漫反射光源做了积分
            	float3 Diffuse_Indirect_lightSource;
				Diffuse_Indirect_lightSource = irradianceSH * BaseColor / PI;
            	
            	Diffuse_Indirect = Diffuse_Indirect_lightSource * KD_IndirectLight; //通过辐照度求出间接光的漫反射
            	//------------------------------间接光-----漫反射------------------------------
            	//------------------------------间接光-----高光------------------------------
            	float3 Specular_Indirect;
            	
            	//间接光高光分为两部分计算,两部分为相乘关系,第一部分是输入光的积分,第二部分是数值模拟或预计算采样

            	//间接光第一部分--输入光的积分:mipmap采样反射探针获得近似结果+AdditionalLights
            	float3 Specular_Indirect_lightSource_integral;
            	//  间接光高光对于输入光积分的近似,以cubemap的mipmap采样方式近似
                float3 R = reflect(-viewDirWS, normalWS);
            	//反射方向和计算mipmap进行采样
                float mip = Roughness * (1.7 - 0.7 * Roughness) * UNITY_SPECCUBE_LOD_STEPS;
                //这里的unity_SpecCube0是反射探针,最多访问两个最近的反射探针。
                float4 rgb_mip = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, R, mip);
                //解码mipmap采样后的探针数据,也就是间接光高光的积分近似,unity_SpecCube0_HDR:HDR environment map decode instructions
            	Specular_Indirect_lightSource_integral = DecodeHDREnvironment(rgb_mip, unity_SpecCube0_HDR) + VertexLighting(i.posWS, i.normalWS);
	
            	//间接光第二部分--数值模拟或预计算采样
            	float3 Specular_Indirect_PartTwo;

            	#ifdef ENABLE_BRDF_LUT
                    //预计算采样
                    float2 env_brdf = tex2D(_BRDFLut, float2(lerp(0, 0.99, NV), lerp(0, 0.99, Roughness))).rg;
                #else
                    //数值近似
                    float2 env_brdf = EnvBRDFApprox(Roughness, NV_WS);
                #endif
            	Specular_Indirect_PartTwo = F_IndirectLight * env_brdf.r + env_brdf.g;
            	
            	Specular_Indirect = Specular_Indirect_lightSource_integral * Specular_Indirect_PartTwo;
            	//------------------------------间接光-----高光------------------------------
            	//------------------------------间接光-----最后计算------------------------------
            	float3 IndirectLightFinal = (Diffuse_Indirect + Specular_Indirect) * AO;  //计算ao影响
                //------------------------------间接光-----最后计算------------------------------
            	//------------------------------最终输出------------------------------
                float4 FinalColor = 0;
                FinalColor.rgb = DirectLightFinal + IndirectLightFinal;
                FinalColor.rgb += Emission;//添加自发光
                return FinalColor;
            }
            ENDHLSL
        }

		// 阴影投射
        Pass
        {
            Name "ShadowCaster"
            Tags
            {
                "LightMode" = "ShadowCaster"
            }

            // -------------------------------------
            // Render State Commands
            ZWrite On
            ZTest LEqual
            ColorMask 0
            Cull[_Cull]

            HLSLPROGRAM
            #pragma target 2.0

            // -------------------------------------
            // Shader Stages
            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"

            // -------------------------------------
            // Universal Pipeline keywords

            // -------------------------------------
            // Unity defined keywords
            #pragma multi_compile_fragment _ LOD_FADE_CROSSFADE

            // This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

            // -------------------------------------
            // Includes
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }

        // 绘制到_CameraNormalsTexture纹理
        Pass
        {
            Name "DepthNormals"
            Tags
            {
                "LightMode" = "DepthNormals"
            }

            // -------------------------------------
            // Render State Commands
            ZWrite On
            Cull[_Cull]

            HLSLPROGRAM
            #pragma target 2.0

            // -------------------------------------
            // Shader Stages
            #pragma vertex DepthNormalsVertex
            #pragma fragment DepthNormalsFragment

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local _NORMALMAP
            #pragma shader_feature_local _PARALLAXMAP
            #pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            // -------------------------------------
            // Unity defined keywords
            #pragma multi_compile_fragment _ LOD_FADE_CROSSFADE

            // -------------------------------------
            // Universal Pipeline keywords
            #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"

            // -------------------------------------
            // Includes
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitDepthNormalsPass.hlsl"
            ENDHLSL
        }

        // 常规渲染过程中不使用,仅用于光照贴图烘焙。
        Pass
        {
            Name "Meta"
            Tags
            {
                "LightMode" = "Meta"
            }

            // -------------------------------------
            // Render State Commands
            Cull Off

            HLSLPROGRAM
            #pragma target 2.0

            // -------------------------------------
            // Shader Stages
            #pragma vertex UniversalVertexMeta
            #pragma fragment UniversalFragmentMetaLit

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature_local_fragment _SPECULAR_SETUP
            #pragma shader_feature_local_fragment _EMISSION
            #pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
            #pragma shader_feature_local_fragment _ALPHATEST_ON
            #pragma shader_feature_local_fragment _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
            #pragma shader_feature_local_fragment _SPECGLOSSMAP
            #pragma shader_feature EDITOR_VISUALIZATION

            // -------------------------------------
            // Includes
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitMetaPass.hlsl"

            ENDHLSL
        }
    }
    Fallback off
}

依赖库:CustomTessellation.hlsl、CommonHlslInc.hlsl

 

#ifndef CommonHlslInc
#define CommonHlslInc

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

//输入结构
struct MeshData
{
    float4 vertex : POSITION;
    float2 uv1 : TEXCOORD0;
    float2 uv2 : TEXCOORD1;
    float4 tangentTS :TANGENT;
    float3 normalTS : NORMAL;
    float4 vertexColor : COLOR;
};

//传递结构
struct VOutData
{
    float4 pos : SV_POSITION; // 必须命名为pos ,因为 TRANSFER_VERTEX_TO_FRAGMENT 是这么命名的,为了正确地获取到Shadow
    float2 uv1 : TEXCOORD0;
    float2 uv2 : TEXCOORD1;
    float4 vertexColor : TEXCOORD2;
    float3 posOS : TEXCOORD3;
    float3 normalTS : TEXCOORD4;
    float3 tangentTS : TEXCOORD5;
    float3 posWS : TEXCOORD6;
    float3 normalWS : TEXCOORD7;
    float3 tangentWS : TEXCOORD8;
    float3 bitangentWS : TEXCOORD9;
    float3 posVS : TEXCOORD10;
};

//传递结构赋值
VOutData FillBaseV2FData(MeshData input)
{
    VOutData output;
    output.pos = TransformObjectToHClip(input.vertex);
    output.uv1 = input.uv1;
    output.uv2 = input.uv2;
    output.vertexColor = input.vertexColor;
    output.posOS = input.vertex.xyz;
    output.normalTS = input.normalTS;
    output.tangentTS = input.tangentTS;
    output.posWS = mul(unity_ObjectToWorld, input.vertex);
    output.normalWS = normalize(TransformObjectToWorldNormal(input.normalTS));
    output.tangentWS = normalize(TransformObjectToWorldNormal(input.tangentTS));
    output.bitangentWS = cross(output.normalWS, output.tangentWS) * input.tangentTS.w; //乘上input.tangentTS.w 是unity引擎的bug,有的模型是 1 有的模型是 -1,必须这么写
    output.posVS = TransformWorldToView(output.posWS);
    return output;
}
#endif

 

#ifndef _CUSTOM_TESSELLATION_INCLUDED
#define _CUSTOM_TESSELLATION_INCLUDED

// Tessellation programs based on this article by Catlike Coding:
// https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/
// 关于参数的详细说明
// https://zhuanlan.zhihu.com/p/479792793

#include  "Assets/TA/ShaderLib/HlslInclude/CommonHlslInc.hlsl"

//细分切割函数对于每个面片调用一次,对于每一个面片,比如三角形:每条边需要一个切割因子,内部需要一个切割因子
struct TessellationFactors {
    float edge[3] : SV_TessFactor;  //  对应三条边的切割因子
    float inside : SV_InsideTessFactor;  //  对应内部的切割因子
};

float4 _TessellationUniform;  //  细分因子,当值为1时不发生细分切割。因子可以分别设置,比如边因子是1内部因子是5,那就不会在边上生成顶点,会在中心生成五个顶点
//自定义的细分切割方法,每个面片调用一次
TessellationFactors patchConstantFunction (InputPatch<MeshData, 3> patch)
{
    TessellationFactors f;
    f.edge[0] = _TessellationUniform.x;
    f.edge[1] = _TessellationUniform.y;
    f.edge[2] = _TessellationUniform.z;
    f.inside = _TessellationUniform.w;
    return f;
}

#endif

在unity中配置天空盒,适当设置后处理后,效果大致如下:

 天空盒资源可以去这个网站下载:HDRIs • Poly Haven

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1595965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode-143. 重排链表【栈 递归 链表 双指针】

LeetCode-143. 重排链表【栈 递归 链表 双指针】 题目描述&#xff1a;解题思路一&#xff1a;找到中点&#xff0c;翻转后半段链表。然后依次改变指针顺序即可。解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个单链表 L 的头节点 head &#…

更优性能与性价比,从自建 ELK 迁移到 SLS 开始

作者&#xff1a;荆磊 背景 ELK (Elasticsearch、Logstash、Kibana) 是当下开源领域主流的日志解决方案&#xff0c;在可观测场景下有比较广泛的应用。 随着数字化进程加速&#xff0c;机器数据日志增加&#xff0c;自建 ELK 在面临大规模数据、查询性能等方面有较多问题和挑…

Ubuntu20.04版本部署MySQL8.0关闭库名表名区分大小写和自定义数据目录(datadir)路径

本篇文章记录关闭数据库表名库名区分大小写和设置自定义数据目录&#xff0c;安装时建议一个一个步骤进行&#xff0c;这样比较容易成功&#xff0c;下面是设置关闭区分表名库名大小写的。 一、关闭库名表名区分大小写 1、先安装数据库 步骤如下&#xff1a; # 第一步:更新…

利用遥感技术反演地表温度的方法与意义

​随着科技的不断进步&#xff0c;遥感技术在地球科学领域的应用变得越来越广泛。其中&#xff0c;利用遥感技术反演地表温度已经成为了一种常见的方法&#xff0c;对于环境监测、气候研究、城市规划等领域具有重要意义。 ​地表温度是指地表或地表以下一定深度范围内的温度&am…

一文了解AI边缘计算盒子是什么产品设备

大家听说过AI边缘计算盒子吗&#xff1f;不知道你有没有注意到&#xff0c;最近这款产品设备在科技圈内可是火得不要不要的&#xff01;那么&#xff0c;它究竟是什么东西呢&#xff1f;别着急&#xff0c;小编我今天就来给大家揭晓。 边缘计算盒子是什么? 边缘计算盒子是一种…

Web项目性能测试 —— 性能分析

从结果摘要、并发数、平均事务响应时间、每秒点击数、业务成功率、系统资源、网页细分图、Web服务器资源、数据库服务器资源等几个方面分析&#xff0c;如图1- 1所示。 性能测试结果分析的一个重要的原则是以性能测试的需求指标为导向。我们回顾一下本次性能测试的目的&#x…

C#硬件接口开发------一文了解WMI

&#x1f388;个人主页&#xff1a;靓仔很忙i &#x1f4bb;B 站主页&#xff1a;&#x1f449;B站&#x1f448; &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;C# 硬件接口开发 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足…

【数据结构】第三节:单链表

前言 本篇要求掌握的C语言基础知识&#xff1a;指针、结构体 目录 前言 单链表 概念 对比链表和顺序表 创建链表 实现单链表 准备工作 打印链表 创建节点并初始化 尾插 二级指针的调用 尾插代码 头插 尾删 头删 查找&#xff08;返回节点&#xff09; 在指定位…

C++生成动态库,C++和C#以及Java在windows和linux调用

Windows生成dllC库 1、创建动态链接库项目 源文件编写函数 // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "pch.h"int sum(int a, int b) {return a b; }BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) {switch…

震动Github榜!7K Star火爆的数字人竟然开源了,拿走不谢(文末福利免费领)

本号专注于分享Github和Gitee上的高质量开源项目&#xff0c;并致力于推动前沿技术的分享。 软件介绍 Fay数字人框架-带货版是一个用于构建数字人应用场景的开源项目&#xff0c;具有低耦合度的各功能模块。你可以轻松更换声音来源、语音识别、情绪分析、NLP处理、情绪语音合成…

内网渗透-内网环境下的横向移动总结

内网环境下的横向移动总结 文章目录 内网环境下的横向移动总结前言横向移动威胁 威胁密码安全 威胁主机安全 威胁信息安全横向移动威胁的特点 利用psexec 利用psexec.exe工具msf中的psexec 利用windows服务 sc命令 1.与靶机建立ipc连接2.拷贝exe到主机系统上3.在靶机上创建一个…

【我的代码生成器】生成React页面类

有了数据表的结构信息&#xff0c;就能生成React 的页面类&#xff0c;快捷方便。 生成界面如下&#xff1a; 生成的React FrmUser.js页面如下&#xff1a; 只需再写里面的操作逻辑代码。

SinoDB创建、更改、删除索引

SinoDB数据库的索引组织方式有两种&#xff1a;B树索引与R树索引。B 树是大多数数据库所采用的索引组织方式。R 树索引作为表的辅助访问方法&#xff0c;主要用于查找多维空间数据。本文主要讨论B 树索引。 1. B 树索引 B 树索引按级别进行组织。最高级别包含指向真实的数据的…

家庭网络防御系统搭建-将NDR系统的zeek日志集成到security onion

在前面的文章中安装了zeek,这里&#xff0c;安装了securityonion&#xff0c;这里&#xff0c;本文讲述如何将zeek生成的日志发送到siem security onion之中。 所有日志集成的步骤分为如下几步&#xff1a; 日志收集配置日志发送接收日志解析配置日志展示配置 ZEEK日志收集配…

初识three.js创建第一个threejs3D页面

说到3D&#xff0c;想必大家都能想到three.js&#xff0c;它是由WebGL封装出来的&#xff0c;接下来&#xff0c;我手把手教大家创建一个简单的3D页面 话尽在代码中&#xff0c;哈哈 大家可以复制代码玩一下 <!DOCTYPE html> <html lang"en"><head&…

华为昇腾AI芯片加持,9.1k Star 的 Open-Sora-Plan,国产Sora要来了吗

Aitrainee | 公众号&#xff1a;AI进修生 哇&#xff0c;今天Github趋势榜第一啊&#xff0c;为了重现Sora&#xff0c;北大这个Open-Sora-Plan&#xff0c;希望通过开源社区力量的复现Sora&#xff0c;目前已支持国产AI芯片(华为昇腾&#xff09;&#xff0c;这回不用被卡脖子…

【iOS】SDWebImage源码阅读笔记

文章目录 前言一、设计目的二、特性三、用法1.UITableView 中使用 UIImageViewWebCache2.使用回调Blocks3.SDWebImageManager 的使用4.单独使用 SDWebImageDownloader 异步下载图片5.单独使用 SDImageCache 异步缓存图片 四、实现原理五、具体实现流程sd_setImageWithURLsd_int…

实习僧网站的实习岗位信息分析

目录 背景描述数据说明数据集来源问题描述分析目标以及导入模块1. 数据导入2. 数据基本信息和基本处理3. 数据处理3.1 新建data_clean数据框3.2 数值型数据处理3.2.1 “auth_capital”&#xff08;注册资本&#xff09;3.2.2 “day_per_week”&#xff08;每周工作天数&#xf…

Matlab-AMF算法(自适应中值滤波Adaptive Median Filtering)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 实现原理 AMF&#xff08;Adaptive Median Filter&#xff0c;自适应中值滤波&#xff09;是一种用于图像处理和信号处理的滤波算…

OpenHarmony南向开发案例:【智能垃圾桶】

样例简介 智能垃圾桶可以通过数字管家应用来监测垃圾桶当前可用容量&#xff0c;提醒主人及时处理垃圾&#xff1b;通过日程管家可以实现和其他智能设备联动。 核心组件位置功能距离传感器置于垃圾桶盖内侧感应垃圾量红外传感器置于垃圾桶前端感应是否有人靠近光敏电阻开发板…