下面的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"
}