Unity的URP下各种光照模型实现

回到目录

大家好,我是阿赵

之前介绍过几种常见的光照模型的写法,可以回顾一下
各种光照模型的shader实现
这里再用HLSL写一遍

一、完整的Shader

Shader "azhao/HLSLLight"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_mainColor("MainColor", Color) = (1,1,1,1)
		_lightColor("LightColor", Color) = (1,1,1,1)
		_specColor("SpecColor",Color) = (1,1,1,1)
		_shininess("Shininess",Range(1,20)) = 1
		_tangentVal("TangentVal",Range(0,1)) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"}
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

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

            struct v2f
            {
				float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;      
				float3 worldPos : TEXCOORD1;
				float3 worldNormal : TEXCOORD2;
				float3 worldViewDir : TEXCOORD3;
				float3 worldTangent : TEXCOORD4;
            };
			CBUFFER_START(UnityPerMaterial)
            float4 _MainTex_ST;
			float _shininess;
			float _tangentVal;
			float4 _mainColor;
			float4 _lightColor;
			float4 _specColor;
			CBUFFER_END
			TEXTURE2D(_MainTex);
			SAMPLER(sampler_MainTex); 

			//获取Lambert漫反射值
			float GetLambertDiffuse(float3 worldNormal)
			{
				Light light = GetMainLight();
				float3 lightDir = normalize(light.direction);
				float NDotL = saturate(dot(worldNormal, lightDir));
				return NDotL;
			}

			//获取HalfLambert漫反射值
			float GetHalfLambertDiffuse(float3 worldNormal)
			{
				Light light = GetMainLight();
				float3 lightDir = normalize(light.direction);
				float NDotL = saturate(dot(worldNormal, lightDir));
				float halfVal = NDotL * 0.5 + 0.5;
				return halfVal;
			}

			//获取光线反射方向
			float3 GetReflectDir(float3 worldPos, float3 worldNormal)
			{
				Light light = GetMainLight();
				float3 lightDir = normalize(light.direction);
				float3 reflectDir = normalize(reflect((lightDir * -1.0), worldNormal));
				return reflectDir;
			}

			//获取Phong高光
			float GetPhongSpec(float3 worldPos,float3 viewDir,float3 worldNormal)
			{
				float3 reflectDir = GetReflectDir(worldPos, worldNormal);
				float specDir = saturate(dot(viewDir, reflectDir));
				float specVal = pow(specDir, _shininess);
				return specVal;
			}

			//获取BlinnPhong高光
			float GetBlinnPhongSpec(float3 worldPos,float3 viewDir,float3 worldNormal)
			{
				Light light = GetMainLight();
				float3 lightDir = normalize(light.direction);
				float3 halfDir = normalize((viewDir + lightDir));
				float specDir = max(dot(normalize(worldNormal), halfDir), 0);
				float specVal = pow(specDir, _shininess);
				return specVal;
			}

			//获取Anisortropic各向异性高光
			float GetAnisortropicSpec(float3 worldPos, float3 worldNormal, float3 worldTangent,float3 viewDir)
			{
				float3 binormal = cross(worldNormal, worldTangent);
				float3 lerpVal = normalize(lerp((worldNormal + binormal), binormal, _tangentVal));
				Light light = GetMainLight();
				float3 lightDir = normalize(light.direction);
				float3 halfDir = normalize(viewDir + lightDir);
				float anistropicVal = dot(lerpVal, halfDir);
				float specDir = max(anistropicVal*anistropicVal, 0);
				float specVal = pow(specDir, _shininess);
				return specVal;
			}


            v2f vert (appdata v)
            {
				v2f o;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
				o.pos = vertexInput.positionCS;
				o.worldPos = vertexInput.positionWS;
				o.worldNormal = TransformObjectToWorldNormal(v.normal);
				o.worldViewDir = normalize(GetCameraPositionWS().xyz - TransformObjectToWorld(v.vertex.xyz));
				o.worldTangent = TransformObjectToWorldDir(v.tangent);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                half4 col = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, i.uv);

				//和之前一样,漫反射光照模型和高光光照模型可以进行选择

				float diffuseVal = GetLambertDiffuse(i.worldNormal);//获取Lambert漫反射值
				//float diffuseVal = GetHalfLambertDiffuse(i.worldNormal);//获取HalfLambert漫反射值

				//float specVal = 0;//无高光
				float specVal = GetPhongSpec(i.worldPos,i.worldViewDir,i.worldNormal);//获取Phong高光
				//float specVal = GetBlinnPhongSpec(i.worldPos, i.worldViewDir, i.worldNormal);//获取BlinnPhong高光
				//float specVal = GetAnisortropicSpec(i.worldPos, i.worldNormal, i.worldTangent, i.worldViewDir);//获取Anisortropic各向异性高光

				//计算漫反射颜色
				float3 diffuseCol = _mainColor * _lightColor*diffuseVal;
				//计算高光颜色
				float3 specCol = specVal * _specColor* _lightColor;
				//最终颜色是环境色+漫反射+高光
				half3 finalCol = UNITY_LIGHTMODEL_AMBIENT + diffuseCol + specCol;

                return half4(finalCol,col.a);
            }
            ENDHLSL
        }
    }
}

二、需要注意的地方

由于这些光照模型的计算方式在之前的一篇文章里面已经说过了,所以就不再一一介绍,只说一下变化在哪里。
1、由于要使用光照方向,所以要引入Lighting.hlsl
2、获取光照信息

Light light = GetMainLight();
float3 lightDir = normalize(light.direction);

其中GetMainLight和结构体Light都是Lighting.hlsl里面的内容

Light GetMainLight()
{
    Light light;
    light.direction = _MainLightPosition.xyz;
    // unity_LightData.z is 1 when not culled by the culling mask, otherwise 0.
    light.distanceAttenuation = unity_LightData.z;
#if defined(LIGHTMAP_ON) || defined(_MIXED_LIGHTING_SUBTRACTIVE)
    // unity_ProbesOcclusion.x is the mixed light probe occlusion data
    light.distanceAttenuation *= unity_ProbesOcclusion.x;
#endif
    light.shadowAttenuation = 1.0;
    light.color = _MainLightColor.rgb;

    return light;
}
struct Light
{
    half3   direction;
    half3   color;
    half    distanceAttenuation;
    half    shadowAttenuation;
};

3、坐标/发现/向量的转换
在GetVertexPositionInputs方法的实现里面,已经介绍过几种转换的方法:

VertexPositionInputs GetVertexPositionInputs(float3 positionOS)
{
    VertexPositionInputs input;
    input.positionWS = TransformObjectToWorld(positionOS);
    input.positionVS = TransformWorldToView(input.positionWS);
    input.positionCS = TransformWorldToHClip(input.positionWS);

    float4 ndc = input.positionCS * 0.5f;
    input.positionNDC.xy = float2(ndc.x, ndc.y * _ProjectionParams.x) + ndc.w;
    input.positionNDC.zw = input.positionCS.zw;

    return input;
}

其中这几个就是转换坐标系,物体局部坐标、观察空间坐标、裁剪空间坐标,转来转去:
TransformObjectToWorld
TransformWorldToView
TransformWorldToHClip
这里还用到了这两个:
如果转换的是法线(Normal),可以用TransformObjectToWorldNormal
如果转换的是切线(Tangent),可以用TransformObjectToWorldDir

猜你喜欢

转载自blog.csdn.net/liweizhao/article/details/130757551