文章目录
- 前言
- 现在我们主要来看Standard的 漫反射 和 镜面反射
- 一、PBS的核心计算BRDF
- 二、Standard的镜面高光颜色
- 三、具体的BRDF计算
- 对于BRDF的具体计算,在下篇文章中,继续解析
- 四、最终代码
- .cginc文件
- Shader文件
前言
在上篇文章中,我们解析了Standard的GI实现,这篇文章我们来解析一下Standard的PBS计算。
- Unity中Shader的Standard材质解析(二)
上篇文章中,主要解析了这个公式 GI漫反射 和 GI镜面反射
现在我们主要来看Standard的 漫反射 和 镜面反射
一、PBS的核心计算BRDF
LightingStandard1(o, worldViewDir, gi);
- 在该函数中,主要进行了如下计算:
二、Standard的镜面高光颜色
s.Albedo = DiffuseAndSpecularFromMetallic1 (s.Albedo, s.Metallic, /out/ specColor, /out/ oneMinusReflectivity);
这里漫反射的反射率(oneMinusReflectivity)推导公式如下:
lerp(A,B,v) = A + v(B - A)
三、具体的BRDF计算
- #error 错误信息 : 会在控制台输出错误信息,并且报错后Shader不会渲染
我们在片元着色器返回物体颜色前测试一下:
可以看见我们控制台输出了错误信息:
对于BRDF的具体计算,在下篇文章中,继续解析
- UNITY_PBS_USE_BRDF3
- UNITY_PBS_USE_BRDF2
- UNITY_PBS_USE_BRDF1
四、最终代码
.cginc文件
#ifndef MYPHYSICALLYBASERENDERING_INCLUDE
#define MYPHYSICALLYBASERENDERING_INCLUDE
//Standard的漫反射和镜面反射计算↓
// Main Physically Based BRDF
// Derived from Disney work and based on Torrance-Sparrow micro-facet model
//
// BRDF = kD / pi + kS * (D * V * F) / 4
// I = BRDF * NdotL
//
// * NDF (depending on UNITY_BRDF_GGX):
// a) Normalized BlinnPhong
// b) GGX
// * Smith for Visiblity term
// * Schlick approximation for Fresnel
half4 BRDF1_Unity_PBS1 (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi)
{
float perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
float3 halfDir = Unity_SafeNormalize (float3(light.dir) + viewDir);
// NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping
// In this case normal should be modified to become valid (i.e facing camera) and not cause weird artifacts.
// but this operation adds few ALU and users may not want it. Alternative is to simply take the abs of NdotV (less correct but works too).
// Following define allow to control this. Set it to 0 if ALU is critical on your platform.
// This correction is interesting for GGX with SmithJoint visibility function because artifacts are more visible in this case due to highlight edge of rough surface
// Edit: Disable this code by default for now as it is not compatible with two sided lighting used in SpeedTree.
#define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0
#if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV
// The amount we shift the normal toward the view vector is defined by the dot product.
half shiftAmount = dot(normal, viewDir);
normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
// A re-normalization should be applied here but as the shift is small we don't do it to save ALU.
//normal = normalize(normal);
float nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here
#else
half nv = abs(dot(normal, viewDir)); // This abs allow to limit artifact
#endif
float nl = saturate(dot(normal, light.dir));
float nh = saturate(dot(normal, halfDir));
half lv = saturate(dot(light.dir, viewDir));
half lh = saturate(dot(light.dir, halfDir));
// Diffuse term
half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;
// Specular term
// HACK: theoretically we should divide diffuseTerm by Pi and not multiply specularTerm!
// BUT 1) that will make shader look significantly darker than Legacy ones
// and 2) on engine side "Non-important" lights have to be divided by Pi too in cases when they are injected into ambient SH
float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
#if UNITY_BRDF_GGX
// GGX with roughtness to 0 would mean no specular at all, using max(roughness, 0.002) here to match HDrenderloop roughtness remapping.
roughness = max(roughness, 0.002);
float V = SmithJointGGXVisibilityTerm (nl, nv, roughness);
float D = GGXTerm (nh, roughness);
#else
// Legacy
half V = SmithBeckmannVisibilityTerm (nl, nv, roughness);
half D = NDFBlinnPhongNormalizedTerm (nh, PerceptualRoughnessToSpecPower(perceptualRoughness));
#endif
float specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later
# ifdef UNITY_COLORSPACE_GAMMA
specularTerm = sqrt(max(1e-4h, specularTerm));
# endif
// specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value
specularTerm = max(0, specularTerm * nl);
#if defined(_SPECULARHIGHLIGHTS_OFF)
specularTerm = 0.0;
#endif
// surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(roughness^2+1)
half surfaceReduction;
# ifdef UNITY_COLORSPACE_GAMMA
surfaceReduction = 1.0-0.28*roughness*perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
# else
surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1]
# endif
// To provide true Lambert lighting, we need to be able to kill specular completely.
specularTerm *= any(specColor) ? 1.0 : 0.0;
half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
half3 color = diffColor * (gi.diffuse + light.color * diffuseTerm)
+ specularTerm * light.color * FresnelTerm (specColor, lh)
+ surfaceReduction * gi.specular * FresnelLerp (specColor, grazingTerm, nv);
return half4(color, 1);
}
// Based on Minimalist CookTorrance BRDF
// Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
//
// * NDF (depending on UNITY_BRDF_GGX):
// a) BlinnPhong
// b) [Modified] GGX
// * Modified Kelemen and Szirmay-Kalos for Visibility term
// * Fresnel approximated with 1/LdotH
half4 BRDF2_Unity_PBS1 (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi)
{
float3 halfDir = Unity_SafeNormalize (float3(light.dir) + viewDir);
half nl = saturate(dot(normal, light.dir));
float nh = saturate(dot(normal, halfDir));
half nv = saturate(dot(normal, viewDir));
float lh = saturate(dot(light.dir, halfDir));
// Specular term
half perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
#if UNITY_BRDF_GGX
// GGX Distribution multiplied by combined approximation of Visibility and Fresnel
// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
// https://community.arm.com/events/1155
float a = roughness;
float a2 = a*a;
float d = nh * nh * (a2 - 1.f) + 1.00001f;
#ifdef UNITY_COLORSPACE_GAMMA
// Tighter approximation for Gamma only rendering mode!
// DVF = sqrt(DVF);
// DVF = (a * sqrt(.25)) / (max(sqrt(0.1), lh)*sqrt(roughness + .5) * d);
float specularTerm = a / (max(0.32f, lh) * (1.5f + roughness) * d);
#else
float specularTerm = a2 / (max(0.1f, lh*lh) * (roughness + 0.5f) * (d * d) * 4);
#endif
// on mobiles (where half actually means something) denominator have risk of overflow
// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
#if defined (SHADER_API_MOBILE)
specularTerm = specularTerm - 1e-4f;
#endif
#else
// Legacy
half specularPower = PerceptualRoughnessToSpecPower(perceptualRoughness);
// Modified with approximate Visibility function that takes roughness into account
// Original ((n+1)*N.H^n) / (8*Pi * L.H^3) didn't take into account roughness
// and produced extremely bright specular at grazing angles
half invV = lh * lh * smoothness + perceptualRoughness * perceptualRoughness; // approx ModifiedKelemenVisibilityTerm(lh, perceptualRoughness);
half invF = lh;
half specularTerm = ((specularPower + 1) * pow (nh, specularPower)) / (8 * invV * invF + 1e-4h);
#ifdef UNITY_COLORSPACE_GAMMA
specularTerm = sqrt(max(1e-4f, specularTerm));
#endif
#endif
#if defined (SHADER_API_MOBILE)
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
#endif
#if defined(_SPECULARHIGHLIGHTS_OFF)
specularTerm = 0.0;
#endif
// surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(realRoughness^2+1)
// 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
// 1-x^3*(0.6-0.08*x) approximation for 1/(x^4+1)
#ifdef UNITY_COLORSPACE_GAMMA
half surfaceReduction = 0.28;
#else
half surfaceReduction = (0.6-0.08*perceptualRoughness);
#endif
surfaceReduction = 1.0 - roughness*perceptualRoughness*surfaceReduction;
half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
half3 color = (diffColor + specularTerm * specColor) * light.color * nl
+ gi.diffuse * diffColor
+ surfaceReduction * gi.specular * FresnelLerpFast (specColor, grazingTerm, nv);
return half4(color, 1);
}
sampler2D_float unity_NHxRoughness1;
half3 BRDF3_Direct1(half3 diffColor, half3 specColor, half rlPow4, half smoothness)
{
half LUT_RANGE = 16.0; // must match range in NHxRoughness() function in GeneratedTextures.cpp
// Lookup texture to save instructions
half specular = tex2D(unity_NHxRoughness1, half2(rlPow4, SmoothnessToPerceptualRoughness(smoothness))).r * LUT_RANGE;
#if defined(_SPECULARHIGHLIGHTS_OFF)
specular = 0.0;
#endif
return diffColor + specular * specColor;
}
half3 BRDF3_Indirect1(half3 diffColor, half3 specColor, UnityIndirect indirect, half grazingTerm, half fresnelTerm)
{
half3 c = indirect.diffuse * diffColor;
c += indirect.specular * lerp (specColor, grazingTerm, fresnelTerm);
return c;
}
// Old school, not microfacet based Modified Normalized Blinn-Phong BRDF
// Implementation uses Lookup texture for performance
//
// * Normalized BlinnPhong in RDF form
// * Implicit Visibility term
// * No Fresnel term
//
// TODO: specular is too weak in Linear rendering mode
half4 BRDF3_Unity_PBS1 (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
float3 normal, float3 viewDir,
UnityLight light, UnityIndirect gi)
{
float3 reflDir = reflect (viewDir, normal);
half nl = saturate(dot(normal, light.dir));
half nv = saturate(dot(normal, viewDir));
// Vectorize Pow4 to save instructions
half2 rlPow4AndFresnelTerm = Pow4 (float2(dot(reflDir, light.dir), 1-nv)); // use R.L instead of N.H to save couple of instructions
half rlPow4 = rlPow4AndFresnelTerm.x; // power exponent must match kHorizontalWarpExp in NHxRoughness() function in GeneratedTextures.cpp
half fresnelTerm = rlPow4AndFresnelTerm.y;
half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
half3 color = BRDF3_Direct1(diffColor, specColor, rlPow4, smoothness);
color *= light.color * nl;
color += BRDF3_Indirect1(diffColor, specColor, gi, grazingTerm, fresnelTerm);
return half4(color, 1);
}
// Default BRDF to use:
//在 ProjectSetting->Graphics->TierSetting中设置
//StandardShaderQuality = low(UNITY_PBS_USE_BRDF3)
//StandardShaderQuality = Medium(UNITY_PBS_USE_BRDF2)
//StandardShaderQuality = High(UNITY_PBS_USE_BRDF1)
#if !defined (UNITY_BRDF_PBS1) // allow to explicitly override BRDF in custom shader
// still add safe net for low shader models, otherwise we might end up with shaders failing to compile
#if SHADER_TARGET < 30 || defined(SHADER_TARGET_SURFACE_ANALYSIS) // only need "something" for surface shader analysis pass; pick the cheap one
#define UNITY_BRDF_PBS1 BRDF3_Unity_PBS1 //效果最差的BRDF
#elif defined(UNITY_PBS_USE_BRDF3)
#define UNITY_BRDF_PBS1 BRDF3_Unity_PBS1
#elif defined(UNITY_PBS_USE_BRDF2)
#define UNITY_BRDF_PBS1 BRDF2_Unity_PBS1
#elif defined(UNITY_PBS_USE_BRDF1)
#define UNITY_BRDF_PBS1 BRDF1_Unity_PBS1
#else
#error something broke in auto-choosing BRDF
#endif
#endif
inline half OneMinusReflectivityFromMetallic1(half metallic)
{
// We'll need oneMinusReflectivity, so
// 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
// store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then
// 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
// = alpha - metallic * alpha
half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;
return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}
inline half3 DiffuseAndSpecularFromMetallic1 (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
{
//计算镜面高光颜色
//当metallic为0(即非金属时),返回unity_ColorSpaceDielectricSpec.rgb(0.04)
//unity_ColorSpaceDielectricSpec.rgb表示的是绝缘体的通用反射颜色
//迪士尼经大量测量用 0.04 来表示
//当 metallic = 1 时(金属),返回Albedo,也就是物体本身的颜色
specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
oneMinusReflectivity = OneMinusReflectivityFromMetallic1(metallic);
return albedo * oneMinusReflectivity;
}
//s : 物体表面数据信息
//viewDir : 视线方向
//gi : 全局光照(GI漫反射 和 GI镜面反射)
inline half4 LightingStandard1 (SurfaceOutputStandard s, float3 viewDir, UnityGI gi)
{
s.Normal = normalize(s.Normal);
half oneMinusReflectivity;
//镜面高光颜色
half3 specColor;
s.Albedo = DiffuseAndSpecularFromMetallic1 (s.Albedo, s.Metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
// shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
// this is necessary to handle transparency in physically correct way - only diffuse component gets affected by alpha
//当开启半透明模式时,对 Alpha 进行相关计算
half outputAlpha;
s.Albedo = PreMultiplyAlpha (s.Albedo, s.Alpha, oneMinusReflectivity, /*out*/ outputAlpha);
//具体的BRDF计算
//s.Albedo : 物体表面的基础颜色
//specColor : 镜面反射颜色
//oneMinusReflectivity : 漫反射率 = 1 - 镜面反射率
//s.Smoothness : 物体表面的光滑度
//s.Normal : 物体表面的法线
//viewDir : 视线方向
//gi.light : 直接光信息
//gi.indirect : GI间接光信息
half4 c = UNITY_BRDF_PBS1 (s.Albedo, specColor, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect);
c.a = outputAlpha;
return c;
}
//Standard的GI计算↓
half3 Unity_GlossyEnvironment1 (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn)
{
half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ;
// TODO: CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!
// For now disabled
#if 0
float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
const float fEps = 1.192092896e-07F; // smallest such that 1.0+FLT_EPSILON != 1.0 (+1e-4h is NOT good here. is visibly very wrong)
float n = (2.0/max(fEps, m*m))-2.0; // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf
n /= 4; // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.html
perceptualRoughness = pow( 2/(n+2), 0.25); // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)
#else
// MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does.
//r = r * (1.7 - 0.7*r)
//由于粗糙度与反射探针的mip变化不呈现线性正比,所以需要一个公式来改变
perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);
#endif
//UNITY_SPECCUBE_LOD_STEPS = 6,表示反射探针的mip级别有 6 档。粗糙度X6得到最终得mip级别
half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);
half3 R = glossIn.reflUVW;
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);
return DecodeHDR(rgbm, hdr);
}
//GI中的镜面反射
inline half3 UnityGI_IndirectSpecular1(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn)
{
half3 specular;
//如果开启了反射探针的Box Projection
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
// we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice for probe0 and probe1), so keep original to pass into BoxProjectedCubemapDirection
half3 originalReflUVW = glossIn.reflUVW;
glossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);
#endif
#ifdef _GLOSSYREFLECTIONS_OFF
specular = unity_IndirectSpecColor.rgb;
#else
half3 env0 = Unity_GlossyEnvironment1 (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);
//如果开启了反射探针混合
#ifdef UNITY_SPECCUBE_BLENDING
const float kBlendFactor = 0.99999;
float blendLerp = data.boxMin[0].w;
UNITY_BRANCH
if (blendLerp < kBlendFactor)
{
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
glossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]);
#endif
half3 env1 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1,unity_SpecCube0), data.probeHDR[1], glossIn);
specular = lerp(env1, env0, blendLerp);
}
else
{
specular = env0;
}
#else
specular = env0;
#endif
#endif
return specular * occlusion;
}
inline UnityGI UnityGlobalIllumination1 (UnityGIInput data, half occlusion, half3 normalWorld)
{
return UnityGI_Base(data, occlusion, normalWorld);
}
//GI计算
inline UnityGI UnityGlobalIllumination1 (UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn)
{
//计算得出GI中的漫反射
UnityGI o_gi = UnityGI_Base(data, occlusion, normalWorld);
//计算得出GI中的镜面反射
o_gi.indirect.specular = UnityGI_IndirectSpecular1(data, occlusion, glossIn);
return o_gi;
}
float SmoothnessToPerceptualRoughness1(float smoothness)
{
return (1 - smoothness);
}
Unity_GlossyEnvironmentData UnityGlossyEnvironmentSetup1(half Smoothness, half3 worldViewDir, half3 Normal, half3 fresnel0)
{
Unity_GlossyEnvironmentData g;
//粗糙度
g.roughness /* perceptualRoughness */ = SmoothnessToPerceptualRoughness1(Smoothness);
//反射球的采样坐标
g.reflUVW = reflect(-worldViewDir, Normal);
return g;
}
//PBR光照模型的GI计算
inline void LightingStandard_GI1(
SurfaceOutputStandard s,
UnityGIInput data,
inout UnityGI gi)
{
//如果是延迟渲染PASS并且开启了延迟渲染反射探针的话
#if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERS
gi = UnityGlobalIllumination1(data, s.Occlusion, s.Normal);
#else
//Unity_GlossyEnvironmentData表示GI中的反射准备数据
Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup1(s.Smoothness, data.worldViewDir, s.Normal,
lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo,
s.Metallic));
//进行GI计算并返回输出gi
gi = UnityGlobalIllumination1(data, s.Occlusion, s.Normal, g);
#endif
}
#endif
Shader文件
//Standard材质
Shader "MyShader/P2_2_6"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
[NoScaleOffset]_MetallicTex("Metallic(R) Smoothness(G) AO(B)",2D) = "white" {}
[NoScaleOffset][Normal]_NormalTex("NormalTex",2D) = "bump" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.0
_Metallic ("Metallic", Range(0,1)) = 0.0
_AO("AO",Range(0,1)) = 1.0
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
LOD 200
// ---- forward rendering base pass:
Pass
{
Name "FORWARD"
Tags
{
"LightMode" = "ForwardBase"
}
CGPROGRAM
// compile directives
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#pragma multi_compile_instancing
#pragma multi_compile_fog
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"
#include "CGInclude/MyPhysicallyBasedRendering.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
half _Glossiness;
half _Metallic;
fixed4 _Color;
sampler2D _MetallicTex;
half _AO;
sampler2D _NormalTex;
struct appdata
{
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
fixed4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
// vertex-to-fragment interpolation data
// no lightmaps:
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0; // _MainTex
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
#if UNITY_SHOULD_SAMPLE_SH
half3 sh : TEXCOORD3; // SH
#endif
//切线空间需要使用的矩阵
float3 tSpace0 : TEXCOORD4;
float3 tSpace1 : TEXCOORD5;
float3 tSpace2 : TEXCOORD6;
UNITY_FOG_COORDS(7)
UNITY_SHADOW_COORDS(8)
};
// vertex shader
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
//世界空间下的切线
half3 worldTangent = UnityObjectToWorldDir(v.tangent);
//切线方向
half tangentSign = v.tangent.w * unity_WorldTransformParams.w;
//世界空间下的副切线
half3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
//切线矩阵
o.tSpace0 = float3(worldTangent.x, worldBinormal.x, worldNormal.x);
o.tSpace1 = float3(worldTangent.y, worldBinormal.y, worldNormal.y);
o.tSpace2 = float3(worldTangent.z, worldBinormal.z, worldNormal.z);
o.worldPos.xyz = worldPos;
o.worldNormal = worldNormal;
// SH/ambient and vertex lights
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
o.sh = 0;
// Approximated illumination from non-important point lights
#ifdef VERTEXLIGHT_ON
o.sh += Shade4PointLights (
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, worldPos, worldNormal);
#endif
o.sh = ShadeSHPerVertex (worldNormal, o.sh);
#endif
UNITY_TRANSFER_LIGHTING(o, v.texcoord1.xy);
UNITY_TRANSFER_FOG(o, o.pos); // pass fog coordinates to pixel shader
return o;
}
// fragment shader
fixed4 frag(v2f i) : SV_Target
{
UNITY_EXTRACT_FOG(i);
float3 worldPos = i.worldPos.xyz;
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
SurfaceOutputStandard o;
UNITY_INITIALIZE_OUTPUT(SurfaceOutputStandard, o);
fixed4 mainTex = tex2D(_MainTex, i.uv);
o.Albedo = mainTex.rgb * _Color;
o.Emission = 0.0;
fixed4 metallicTex = tex2D(_MetallicTex, i.uv);
o.Metallic = metallicTex.r * _Metallic;
o.Smoothness = metallicTex.g * _Glossiness;
o.Occlusion = metallicTex.b * _AO;
o.Alpha = 1;
half3 normalTex = UnpackNormal(tex2D(_NormalTex,i.uv));
half3 worldNormal = half3(dot(i.tSpace0,normalTex),dot(i.tSpace1,normalTex),dot(i.tSpace2,normalTex));
o.Normal = worldNormal;
// compute lighting & shadowing factor
UNITY_LIGHT_ATTENUATION(atten, i, worldPos)
// Setup lighting environment
UnityGI gi;
UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = _LightColor0.rgb;
gi.light.dir = _WorldSpaceLightPos0.xyz;
// Call GI (lightmaps/SH/reflections) lighting function
UnityGIInput giInput;
UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
giInput.light = gi.light;
giInput.worldPos = worldPos;
giInput.worldViewDir = worldViewDir;
giInput.atten = atten;
#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
giInput.lightmapUV = IN.lmap;
#else
giInput.lightmapUV = 0.0;
#endif
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = i.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
giInput.probeHDR[0] = unity_SpecCube0_HDR;
giInput.probeHDR[1] = unity_SpecCube1_HDR;
#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
#endif
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
giInput.boxMax[0] = unity_SpecCube0_BoxMax;
giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
giInput.boxMax[1] = unity_SpecCube1_BoxMax;
giInput.boxMin[1] = unity_SpecCube1_BoxMin;
giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
#endif
LightingStandard_GI1(o, giInput, gi);
//return fixed4(gi.indirect.specular,1);
// PBS的核心计算
fixed4 c = LightingStandard1(o, worldViewDir, gi);
UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog
UNITY_OPAQUE_ALPHA(c.a); //把c的Alpha置1
#error 错了没(⊙o⊙)?
return c;
}
ENDCG
}
}
}