下面的shader参照博客修改而成:改动的地方用此颜色表示
代码参照:
unity build-in管线中的PBR材质Shader分析研究_郭大钦的博客-CSDN博客_shader 支持pbr材质以及cubemap unity build-in管线中的PBR材质Shader分析研究_bulit-in pbr-CSDN博客
最终效果如下:左边是手写的,右边是unity内置standard

1.原shader没有阴影,根据精要我在此基础上添加了阴影.
2.原shader由于作者追求尽量与unity内置standard效果一致,所以有些公式是按照unity独特方式写的,而且是最高质量的计算方式 ,因此有些算法还可以精简,再就是他把函数都展开写到片元着色器里面了,比较难看.有待改进.
3.仅有不透明物体的计算方式.
4.金属度,粗糙度只有数值调节,没有贴图控制.待完善.
- Shader "Custom/myPBR"
- {
- Properties
- {
- _Tint("Tint",Color)=(1,1,1,1)
- _MainTex ("Texture", 2D) = "white" {}
- //金属度要经过gama,否则即便是linear空间下渲染,unity也不会对一个滑条做操作的
- [Gamma]_Metallic("Metallic",Range(0,1))=0
- _MetallicGlossMap("Metallic", 2D) = "white" {}
- _Smoothness("Smoothness(Metallic.a)",Range(0,1))=0.5
- _BumpMap("Normal Map", 2D) = "bump" {}
- _Parallax ("Height Scale", Range (0.00, 0.08)) = 0.0
- _ParallaxMap ("Height Map", 2D) = "black" {}
- _OcclusionMap("Occlusion", 2D) = "white" {}
- }
- SubShader
- {
- Pass
- {
- Tags{"LightMode"="ForwardBase"}
- CGPROGRAM
- // 加上下面这行
- #pragma multi_compile_fwdbase
- #pragma vertex vert
- #pragma fragment frag
- //添加lightmap支持
- #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
- #include "UnityStandardBRDF.cginc"
- // 加上下面这行,不然没有阴影
- #include "AutoLight.cginc"
- struct appdata
- {
- float4 vertex : POSITION;
- float3 normal:NORMAL;
- float2 uv : TEXCOORD0;
- float2 uv1:TEXCOORD1;
- fixed4 tangent : TANGENT;
- };
- struct v2f
- {
- float2 uv : TEXCOORD0;
- #ifndef LIGHTMAP_OFF
- half2 uv1:TEXCOORD1;
- #endif
- //float4 vertex : SV_POSITION;
- float4 pos : SV_POSITION;//修改为pos
- float3 normal:TEXCOORD2;
- float3 worldPos:TEXCOORD3;
- float4 tangent:TEXCOORD4;
- float3x3 tangentToWorld : TEXCOORD5;
- float3 viewDir:COLOR1;
- float3x3 tangentMatrix: TEXCOORD8;
- float3 objectspaceViewdir:COLOR2;
- SHADOW_COORDS(11)//三剑客1. 注意括号里面的数字(11)========================================================================================
- };
- sampler2D _MainTex;
- float4 _Tint;
- float _Metallic;
- float _Smoothness;
- float4 _MainTex_ST;
- sampler2D _MetallicGlossMap;
- sampler2D _BumpMap;
- sampler2D _OcclusionMap;
- float _Parallax;
- sampler2D _ParallaxMap;
- inline half OneMinusReflectivityFromMetallic(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 DiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
- {
- specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
- oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic);
- return albedo * oneMinusReflectivity;
- }
- v2f vert (appdata v)
- {
- v2f o;
- //o.vertex = UnityObjectToClipPos(v.vertex);
- o.pos = UnityObjectToClipPos(v.vertex);
- o.worldPos = mul(unity_ObjectToWorld, v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- o.normal = UnityObjectToWorldNormal(v.normal);
- o.normal = normalize(o.normal);
- o.tangent=v.tangent;
- float3 normalWorld = UnityObjectToWorldNormal(v.normal);
- float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
- // 对于奇怪的负缩放,我们需要sign取反(flip the sign)
- half sign = tangentWorld.w * unity_WorldTransformParams.w;
- half3 binormal = cross(normalWorld, tangentWorld) * sign;
- float3x3 tangentToWorld = half3x3(tangentWorld.xyz, binormal, normalWorld);
- o.tangentToWorld=tangentToWorld;
- o.viewDir=normalize(UnityWorldSpaceViewDir(o.worldPos));
- //Parallax viewDir need to changed from ObjectSpace to Tangent
- fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(o.worldPos));
- fixed3 objectspaceViewdir= mul(unity_WorldToObject, worldViewDir);
- o.objectspaceViewdir =normalize(objectspaceViewdir);
- float3 objectSpaceBinormal = normalize(cross(v.normal,v.tangent.xyz) * v.tangent.w);
- float3x3 tangentMatrix = float3x3(v.tangent.xyz, objectSpaceBinormal, v.normal);
- o.tangentMatrix = tangentMatrix;
- #ifndef LIGHTMAP_OFF
- o.uv1 = v.uv1.xy*unity_LightmapST.xy + unity_LightmapST.zw;
- #endif
- TRANSFER_SHADOW(o);//三剑客2================================================================================================================
- //重点!! 由于计算阴影的这些宏会使用上下文的变量进行相关的计算!!!!
- //这里的TRANSFER_SHADOW就会使用到 v.vertex或a.pos来计算
- //因此必须保证自定义的变量名和这些宏使用的变量名相匹配!!!!!!!
- //SO! appdata结构中的顶点坐标必须是 vertex !
- //顶点着色器的输出结构体 v2f 必须命名为 v !
- //v2f中顶点位置必须是pos !
- //否则会出现以下报错
- //invalid subscript 'pos' 'ComputeScreenPos': no matching 1 parameter function
- return o;
- }
- fixed4 frag (v2f i) : SV_Target
- {
- #ifndef LIGHTMAP_OFF
- fixed3 lm = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap,i.uv1));
- float3 albedo = _Tint*tex2D(_MainTex,i.uv);
- float3 finalRes = albedo * lm;
- return float4( finalRes,1);
- #endif
- half height = tex2D(_ParallaxMap, i.uv).g;
- float3 tangentspaceViewDir =normalize( mul(i.tangentMatrix, i.objectspaceViewdir));
- i.uv += ParallaxOffset(height,_Parallax,tangentspaceViewDir);
- _Metallic=tex2D(_MetallicGlossMap,i.uv).r*_Metallic;
- _Smoothness=tex2D(_MetallicGlossMap,i.uv).a*_Smoothness;
- float occlusion=tex2D(_OcclusionMap,i.uv).r;
- float3 normal = normalize(i.normal);//没有加normalize操作,导致其还是取的顶点法线,没有进行插值
- // #ifdef _NORMALMAP
- half3 tangent1 = i.tangentToWorld[0].xyz;
- half3 binormal1 = i.tangentToWorld[1].xyz;
- half3 normal1 = i.tangentToWorld[2].xyz;
- float3 normalTangent =UnpackNormal(tex2D(_BumpMap,i.uv));
- //return float4(1,0,0,1);
- // normal= normalize(float3(dot(i.TtoW0.xyz, normalTangent), dot(i.TtoW1.xyz, normalTangent), dot(i.TtoW2.xyz, normalTangent)));//矩阵变换
- normal=normalize((float3)(tangent1 * normalTangent.x + binormal1 * normalTangent.y + normal1 * normalTangent.z));
- // #endif
- float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
- //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
- // float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
- float3 viewDir=i.viewDir;
- float3 lightColor = _LightColor0.rgb;
- float3 halfVector = normalize(lightDir + viewDir);
- //roughness相关
- float perceptualRoughness = 1 - _Smoothness;
- float roughness = perceptualRoughness * perceptualRoughness;
- roughness=max(roughness,0.002);//即便smoothness为1,也要有点高光在
- float squareRoughness = roughness * roughness;
- float nl = max(saturate(dot(normal , lightDir ) ) , 0.0000001);//防止除0
- float nv = max(saturate(dot(normal, viewDir)), 0.0000001);
- float vh = max(saturate(dot(viewDir, halfVector)), 0.0000001);
- float lh = max(saturate(dot(lightDir, halfVector)), 0.0000001);
- float nh = max(saturate(dot(normal, halfVector)), 0.0000001);
-
- //1.1直接光漫反射部分.兰伯特光照。没有除以π,是因为会显得太暗。
- float3 Albedo = _Tint*tex2D(_MainTex,i.uv);
- //float3 rawDiffColor = DisneyDiffuse(nv,nl,lh,perceptualRoughness)*nl*lightColor;
- float3 rawDiffColor = nl*lightColor;//去掉了影响不大的"高级运算"
- //1.2.直接光镜面反射部分
- // 1.2.1 D项(GGX)
- float D=GGXTerm(nh,roughness);
- // 1.2.2 G项 几何函数,遮蔽变暗一些
- // 直接光照和间接光照时的k都在逼近二分之一,只不过直接光照时这个值最小为八分之一而不是0。这是为了保证在表面绝对光滑时
- // 也会吸收一部分光线,毕竟完全不吸收光线的物体在现实中不存在
- float G=SmithJointGGXVisibilityTerm(nl,nv,roughness);
- //1.2.3 F项 菲涅尔反射 金属反射强边缘反射强
- float3 F0 = lerp(unity_ColorSpaceDielectricSpec.rgb, Albedo, _Metallic);
- float3 F=FresnelTerm(F0,lh);
- //漫反射系数kd
- float3 kd = OneMinusReflectivityFromMetallic(_Metallic);
- kd*=Albedo;
-
- float3 specular = D * G * F ;
- float3 specColor = specular * lightColor*nl*UNITY_PI;//直接光镜面反射部分。镜面反射的系数就是F。漫反射之前少除π了,所以为了保证漫反射和镜面反射的比例,这里还得乘一个π
- float3 diffColor = kd * rawDiffColor;//直接光漫反射部分。
- float3 directLightResult = diffColor + specColor;
- //至此,直接光部分结束
-
-
-
-
-
-
- //2.开始间接光部分:注意间接光的漫反射需要设置光照探针,间接光的镜面反射需要设置反射探针.
- // 2.1间接光漫反射
- half3 iblDiffuse = ShadeSH9(float4(normal,1));
- float3 iblDiffuseResult = iblDiffuse*kd;//乘间接光漫反射系数
- // 2.2间接光镜面反射
- float mip_roughness = perceptualRoughness * (1.7 - 0.7*perceptualRoughness );
- float3 reflectVec = reflect(-viewDir, normal);
- half mip = mip_roughness * UNITY_SPECCUBE_LOD_STEPS;//得出mip层级。默认UNITY_SPECCUBE_LOD_STEPS=6(定义在UnityStandardConfig.cginc)
- half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectVec, mip);//视线方向的反射向量,去取样,同时考虑mip层级
- half3 iblSpecular = DecodeHDR(rgbm, unity_SpecCube0_HDR);//使用DecodeHDR将颜色从HDR编码下解码。可以看到采样出的rgbm是一个4通道的值,
- half surfaceReduction=1.0/(roughness*roughness+1.0);
- float oneMinusReflectivity = unity_ColorSpaceDielectricSpec.a-unity_ColorSpaceDielectricSpec.a*_Metallic; //grazingTerm压暗非金属的边缘异常高亮
- half grazingTerm=saturate(_Smoothness+(1-oneMinusReflectivity));
- float3 iblSpecularResult = surfaceReduction*iblSpecular*FresnelLerp(F0,grazingTerm,nv);
- float3 indirectResult = (iblDiffuseResult + iblSpecularResult)*occlusion;
- //至此,结束间接光部分
-
-
- fixed shadow = SHADOW_ATTENUATION(i);//三剑客3===============================================================================================
- //最终加和
- float3 finalResult = directLightResult*shadow + indirectResult;//在直接光后面加了 *shadow
- return float4(finalResult,1);
-
- }
- ENDCG
- }
- }
- FallBack"Diffuse"
- }