Last active
March 12, 2025 08:46
-
-
Save binaryfoundry/a1eeca36922b71fe1942751879ab8ab6 to your computer and use it in GitHub Desktop.
Unity PBR Shader Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Shader "CustomPBRShader" | |
| { | |
| Properties | |
| { | |
| [NoScaleOffset] _Albedo("Albedo", Color) = (1.0, 1.0, 1.0, 1.0) | |
| [NoScaleOffset] _Metalness("Conductivity", Range(0.0, 1.0)) = 0.0 | |
| [NoScaleOffset] _Roughness("Roughness", Range(0.0, 1.0)) = 0.0 | |
| [NoScaleOffset][HDR] _RadianceMap("RadianceMap", CUBE) = "" {} | |
| [NoScaleOffset][HDR] _IrradianceMap("IrradianceMap", CUBE) = "" {} | |
| } | |
| SubShader | |
| { | |
| Tags { "RenderType" = "Transparent" "Queue" = "Geometry" } | |
| LOD 100 | |
| Pass | |
| { | |
| Blend SrcAlpha OneMinusSrcAlpha | |
| CGPROGRAM | |
| #pragma vertex vert | |
| #pragma fragment frag | |
| #include "UnityCG.cginc" | |
| float4 _Albedo; | |
| float _Metalness; | |
| float _Roughness; | |
| samplerCUBE _RadianceMap; | |
| samplerCUBE _IrradianceMap; | |
| static float MipLevels = 8.0; | |
| static float Exposure = 1.5; | |
| struct appdata | |
| { | |
| float4 vertex : POSITION; | |
| float3 normal : NORMAL; | |
| }; | |
| struct v2f | |
| { | |
| float4 pos : POSITION; | |
| float3 normal : NORMAL; | |
| float4 posWorld : TEXCOORD1; | |
| }; | |
| v2f vert (appdata v) | |
| { | |
| v2f o; | |
| o.posWorld = mul(unity_ObjectToWorld, v.vertex); | |
| o.normal = normalize(mul(float4(v.normal, 0.0), unity_WorldToObject).xyz); | |
| o.pos = UnityObjectToClipPos(v.vertex); | |
| return o; | |
| } | |
| static const float3x3 ACESInputMat = | |
| { | |
| {0.59719, 0.35458, 0.04823}, | |
| {0.07600, 0.90834, 0.01566}, | |
| {0.02840, 0.13383, 0.83777} | |
| }; | |
| static const float3x3 ACESOutputMat = | |
| { | |
| { 1.60475, -0.53108, -0.07367}, | |
| {-0.10208, 1.10813, -0.00605}, | |
| {-0.00327, -0.07276, 1.07602} | |
| }; | |
| float3 RRTAndODTFit(float3 v) | |
| { | |
| float3 a = v * (v + 0.0245786f) - 0.000090537f; | |
| float3 b = v * (0.983729f * v + 0.4329510f) + 0.238081f; | |
| return a / b; | |
| } | |
| float3 toneMapACES(float3 color) | |
| { | |
| color = mul(ACESInputMat, color); | |
| color = RRTAndODTFit(color); | |
| color = mul(ACESOutputMat, color); | |
| color = saturate(color); | |
| return color; | |
| } | |
| float3 EnvBRDFApprox(float3 f0, float3 NoV, float roughness) | |
| { | |
| float4 c0 = float4(-1.0, -0.0275, -0.572, 0.022); | |
| float4 c1 = float4(1.0, 0.0425, 1.04, -0.04); | |
| float4 r = roughness * c0 + c1; | |
| float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; | |
| float2 AB = float2(-1.04, 1.04) * a004 + r.zw; | |
| return f0 * AB.x + AB.y; | |
| } | |
| float3 diffuse_img(float3 n) | |
| { | |
| return texCUBE(_IrradianceMap, n).rgb; | |
| } | |
| float3 specular_img(float3 n, float3 v, float3 r) | |
| { | |
| return texCUBElod(_RadianceMap, float4(r, _Roughness * MipLevels)).rgb; | |
| } | |
| fixed4 frag (v2f i) : SV_Target | |
| { | |
| float3 n = normalize(i.normal); | |
| float3 v = normalize(_WorldSpaceCameraPos - i.posWorld.xyz); | |
| float3 r = reflect(v, n); | |
| float NoV = dot(n, v); | |
| float3 f0 = max(_Albedo.rgb * _Metalness, 0.04); | |
| float3 diffuse = diffuse_img(n) * _Albedo.rgb * (1.0 - _Metalness); | |
| float3 specular = specular_img(n, v, r); | |
| float3 c = lerp(diffuse, specular, EnvBRDFApprox(f0, NoV, _Roughness)); | |
| return float4(toneMapACES(c * exposure), _Albedo.a); | |
| } | |
| ENDCG | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment