Last active
September 29, 2024 15:32
-
-
Save binaryfoundry/a1eeca36922b71fe1942751879ab8ab6 to your computer and use it in GitHub Desktop.
Unity PBR Shader
This file contains 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; | |
} | |
// Tone Mapping | |
//https://mynameismjp.wordpress.com/2010/04/30/a-closer-look-at-tone-mapping/ | |
//https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl | |
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; | |
} | |
// Maps real valued output to a suitable range for display on an | |
// 8-bits per channel RGB screen. | |
float3 toneMapACES(float3 color) | |
{ | |
color = mul(ACESInputMat, color); | |
color = RRTAndODTFit(color); | |
color = mul(ACESOutputMat, color); | |
color = saturate(color); | |
return color; | |
} | |
// Approximates the roughness/Fresnel lookup table generated offline. | |
// Explained in Filament documentation. | |
// https://google.github.io/filament/Filament.md.html#table_texturedfg | |
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; | |
} | |
// Diffuse reflection map. | |
// https://google.github.io/filament/Filament.md.html#toc5.3.4.1 | |
float3 diffuse_img(float3 n) | |
{ | |
return _Albedo.rgb * texCUBE(_IrradianceMap, n).rgb; | |
} | |
// https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Shaders/Private/ReflectionEnvironmentShared.ush | |
// Heuristic that maps roughness to mip level | |
// This is done in a way such that a certain mip level will always have the same roughness, regardless of how many mips are in the texture | |
// Using more mips in the cubemap just allows sharper reflections to be supported | |
//#define REFLECTION_CAPTURE_ROUGHEST_MIP 1 | |
//#define REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE 1.2 | |
//half ComputeReflectionCaptureMipFromRoughness(half Roughness, half CubemapMaxMip) | |
//{ | |
// half LevelFrom1x1 = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(Roughness); | |
// return CubemapMaxMip - 1 - LevelFrom1x1; | |
//} | |
// Diffuse albedo must be divided by pi to be energy conserving. | |
// https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-shading/diffuse-lambertian-shading | |
// Sample offline generated convolved environment map. MipLevels | |
// should match the number of images output. | |
// https://google.github.io/filament/Filament.md.html#toc5.3.4.4 | |
// If not using a pre-convolved environment map, but instead one rendered | |
// at runtime use UE4 heuristic mapping to calculate mip level above. This | |
// mimics the offline convolution by biasing the mip level. | |
float3 specular_img(float3 n, float3 v) | |
{ | |
float3 r = reflect(v, n); | |
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); | |
float NoV = dot(n, v); | |
float3 f0 = max(_Albedo.rgb * _Metalness, 0.04); | |
float3 diffuse = diffuse_img(n) * (1.0 - _Metalness); | |
float3 specular = specular_img(n, v); | |
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