回到目录
大家好,我是阿赵
之前介绍过几种常见的光照模型的写法,可以回顾一下
各种光照模型的shader实现
这里再用HLSL写一遍
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 = 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