I know this is my third forum regarding this but this is gonna be the last one. So I rewrote my code and this time, the result looked better. It doesn’t make the material completely black if the roughness is 1 but, I’m still not sure if this is the desired effect.
Here are some results with different properties. If you see the image with 1 roughness and 1 metallic, the material still does look dark but if you look closely, you can see some texture so it’s not completely black.
The code:
Shader "ShaderChallenge/CustomPBR"
{
Properties
{
_Albedo("Albedo", 2D) = "white" {}
_Roughness("Roughness", Range(0, 1)) = 1.0
_Metallic("Metallic", Range(0, 1)) = 0.0
}
SubShader
{
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityStandardBRDF.cginc"
#define pi 3.14159265359
sampler2D _Albedo;
float _Roughness;
float _Metallic;
struct v2f
{
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
float3 normal : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
// Convert vertex position to world space
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
// Normal vector
o.normal = UnityObjectToWorldNormal(v.normal);
o.uv = v.texcoord;
return o;
}
// Oren-Nayar
float OrenNayar(float3 n, float3 v, float3 l)
{
float nl = dot(n, l);
float nv = dot(n, v);
float anglenl = acos(nl);
float anglenv = acos(nv);
float alpha = max(anglenv, anglenl);
float beta = min(anglenv, anglenl);
float gamma = dot(v - n * nv, l - n * nl);
float a2 = pow(_Roughness, 2.0);
float A = 1.0 - 0.5 * (a2 / (a2 + 0.57));
float B = 0.45 * (a2 / (a2 + 0.09));
float C = sin(alpha) * tan(beta);
float result = max(0.0, nl) * (A + B * max(0.0, gamma) * C);
return result;
}
float DistributionGGX(float3 n, float3 h)
{
float a2 = pow(_Roughness, 2.0);
float nh = dot(n, h);
float nh2 = pow(nh, 2.0);
float num = a2;
float den = (nh2 * (a2 - 1.0) + 1.0);
den = pi * pow(den, 2.0);
return num / den;
}
float GeometrySchlickGGX(float dot, float k)
{
float num = dot;
float den = dot * (1.0 - k) + k;
return num / den;
}
float GeometrySmith(float3 n, float3 v, float3 l)
{
float k = pow(_Roughness + 1.0, 2.0) / 8.0;
float nv = DotClamped(n, v);
float nl = DotClamped(n, l);
float ggx1 = GeometrySchlickGGX(nv, k);
float ggx2 = GeometrySchlickGGX(nl, k);
return ggx1 * ggx2;
}
float FresnelSchlick(float3 n, float3 v, float3 albedo)
{
float cosTheta = dot(n, v);
float3 F0 = 0.04;
F0 = lerp(F0, albedo, _Metallic);
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
float Microfacet(float3 n, float3 h, float3 v, float3 l, float3 albedo)
{
float d = DistributionGGX(h, n);
float g = GeometrySmith(n, v, l);
float f = FresnelSchlick(n, v, albedo);
return (d * g * f) / 4.0 * DotClamped(n, l) * DotClamped(n, v) + 0.001;
}
float4 frag(v2f i) : SV_TARGET
{
float3 lightCol = _LightColor0.rgb;
// Normalize the normal
float3 n = normalize(i.normal);
// Light vector from mesh's surface
float3 l = normalize(_WorldSpaceLightPos0.xyz);
// Viewport(camera) vector from mesh's surface
float3 v = normalize(_WorldSpaceCameraPos - i.worldPos.xyz);
// Halfway vector
float3 h = normalize(l + v);
float3 albedo = tex2D(_Albedo, i.uv).rgb;
float3 ambient = unity_AmbientSky;
float3 specular = Microfacet(n, h, v, l, albedo);
float3 diffuse = ambient + OrenNayar(n, v, l);
diffuse *= 1.0 - _Metallic;
float3 color = albedo * (diffuse * lightCol) + (specular * lightCol);
return float4(color, 1.0);
}
ENDCG
}
}
}