Last active
March 23, 2023 22:11
-
-
Save BlurryLight/b351cd29a21399681df5a1ac66c0b3d3 to your computer and use it in GitHub Desktop.
Unity Naive HBAO
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
#ifndef HBAO_CGINC | |
#define HBAO_CGINC | |
#include "UnityCG.cginc" | |
sampler2D _MainTex; | |
float4 _MainTex_TexelSize; | |
half4x4 _WorldToViewMatrix; | |
struct appdata | |
{ | |
float4 vertex : POSITION; | |
float2 uv : TEXCOORD0; | |
}; | |
struct v2f | |
{ | |
float2 uv : TEXCOORD0; | |
float4 vertex : SV_POSITION; | |
}; | |
v2f HBAO_vert(appdata v) | |
{ | |
v2f o; | |
o.vertex = UnityObjectToClipPos(v.vertex); | |
o.uv = v.uv; | |
return o; | |
} | |
float _AOStrength; | |
float _AORadius; | |
float _AOBias; | |
float _EnableBlur; | |
sampler2D _CameraGBufferTexture2; | |
sampler2D _CameraDepthTexture; | |
float2 _InvTextureSize; | |
float getRawDepth(float2 uv) { return SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, float4(uv, 0.0, 0.0)); } | |
inline float3 FetchViewPos(float2 uv) | |
{ | |
float3 viewSpaceRay = mul(unity_CameraInvProjection, float4(uv * 2.0 - 1.0, 1.0, 1.0) * _ProjectionParams.z); | |
float rawDepth = getRawDepth(uv); | |
return viewSpaceRay * Linear01Depth(rawDepth); | |
} | |
inline float FallOff(float dist) | |
{ | |
return saturate(1 - dist * dist / (_AORadius * _AORadius)); | |
} | |
inline float TanToSin(float x) | |
{ | |
return x / sqrt(x * x + 1.0); | |
} | |
inline float ViewPosTan(float3 V) | |
{ | |
// 对于DirectX平台,Cam坐标系与OpenGL不同,主要是Z轴是反向的 | |
// DirectX中,相机正对的方向是Z轴正向,对于ViewSpace的坐标,Z为负的代表指向相机 | |
// Unity 遵循OpenGL惯例 | |
// Z轴正向指向相机 | |
return V.z; | |
} | |
inline float BiasedViewPosTan(float3 V, float bias) | |
{ | |
//bias [0,1] | |
float tangentBias = tan(bias * 0.5 * UNITY_PI); | |
return ViewPosTan(V) + tangentBias; | |
} | |
float fullAO(float3 pos, float3 stepPos, float3 tangentVec, inout float top) | |
{ | |
float3 h = stepPos - pos; | |
float3 h_dir = normalize(h); | |
float tanH = ViewPosTan(h_dir); | |
float sinH = TanToSin(tanH); | |
float tanT = BiasedViewPosTan(tangentVec, _AOBias); | |
float sinT = TanToSin(tanT); | |
float dist = length(h); | |
float sinBlock = sinH - sinT; | |
float diff = max(sinBlock - top, 0); | |
top = max(sinBlock, top); | |
return diff * FallOff(dist); | |
} | |
// very bad noise | |
// from here https://forum.unity.com/threads/generate-random-float-between-0-and-1-in-shader.610810/ | |
float random(float2 uv) | |
{ | |
return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453123); | |
} | |
float4 HBAO_frag(v2f input) : SV_Target | |
{ | |
float ao = 0; | |
// half depth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, input.uv)); | |
// half3 WorldNormal = tex2D(_CameraGBufferTexture2, input.uv).rgb * 2 - 1; | |
// half3 viewNormal = normalize(mul((half3x3)_WorldToViewMatrix, WorldNormal)); | |
float3 viewPosition = FetchViewPos(input.uv); | |
float rnd = random(input.uv); | |
const int NumDirs = 4; | |
float delta = 2.0 * UNITY_PI / (NumDirs + 1); | |
const int NumSteps = 6; | |
float stepSize = _AORadius / abs(viewPosition.z); | |
// 最大的步进半径除以ViewPos,如果小于1则说明这个区域太远了,AO影响很小 | |
if (stepSize < 1.0) return 1.0; | |
stepSize /= NumSteps; | |
float InitialAngle = delta * rnd; | |
UNITY_UNROLL | |
for (int i = 0; i < NumDirs; i++) | |
{ | |
float angle = InitialAngle + delta * i; | |
float cos, sin; | |
sincos(angle, sin, cos); | |
float2 dir = float2(cos, sin); | |
float rayPixel = 1; | |
float top = 0; | |
float3 tangentVec = | |
FetchViewPos(input.uv + dir * _InvTextureSize) - | |
FetchViewPos(input.uv - dir * _InvTextureSize); | |
tangentVec = normalize(tangentVec); | |
UNITY_UNROLL | |
for (int j = 0; j < NumSteps; ++j) | |
{ | |
float2 stepUV = rayPixel * dir * _InvTextureSize + input.uv; | |
float3 stepViewPos = FetchViewPos(stepUV); | |
ao += fullAO(viewPosition, stepViewPos, tangentVec, top); | |
rayPixel += stepSize; | |
} | |
} | |
ao /= float(NumDirs); | |
ao = ao * _AOStrength; | |
return saturate(1 - ao); | |
} | |
sampler2D _AOTex; | |
float4 HBAO_merge_frag(v2f input) : SV_Target | |
{ | |
float ao = tex2D(_AOTex, input.uv); | |
float4 col = tex2D(_MainTex, input.uv); | |
return float4(col.rgb * ao, col.a); | |
} | |
//9x9 gaussian blur | |
//https://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ | |
float4 horizontal_blur(v2f input) : SV_Target | |
{ | |
const float3 offset = float3(0.0f, 1.3846153846f, 3.2307692308f); | |
const float3 weight = float3(0.2270270270f, 0.3162162162f, 0.0702702703f); | |
float4 centerColor = tex2D(_MainTex, input.uv) * weight.x; | |
centerColor += tex2D(_MainTex, input.uv + float2(0.0, offset.y) * _MainTex_TexelSize.xy) * weight.y; | |
centerColor += tex2D(_MainTex, input.uv - float2(0.0, offset.y) * _MainTex_TexelSize.xy) * weight.y; | |
centerColor += tex2D(_MainTex, input.uv + float2(0.0, offset.z) * _MainTex_TexelSize.xy) * weight.z; | |
centerColor += tex2D(_MainTex, input.uv - float2(0.0, offset.z) * _MainTex_TexelSize.xy) * weight.z; | |
return centerColor; | |
} | |
float4 vertical_blur(v2f input) : SV_Target | |
{ | |
const float3 offset = float3(0.0f, 1.3846153846f, 3.2307692308f); | |
const float3 weight = float3(0.2270270270f, 0.3162162162f, 0.0702702703f); | |
float4 centerColor = tex2D(_MainTex, input.uv) * weight.x; | |
centerColor += tex2D(_MainTex, input.uv + float2(offset.y, 0.0) * _MainTex_TexelSize.xy) * weight.y; | |
centerColor += tex2D(_MainTex, input.uv - float2(offset.y, 0.0) * _MainTex_TexelSize.xy) * weight.y; | |
centerColor += tex2D(_MainTex, input.uv + float2(offset.z, 0.0) * _MainTex_TexelSize.xy) * weight.z; | |
centerColor += tex2D(_MainTex, input.uv - float2(offset.z, 0.0) * _MainTex_TexelSize.xy) * weight.z; | |
return centerColor; | |
} | |
//3x3 gaussian blur | |
float4 tap4blur(v2f input) : SV_Target | |
{ | |
const float4 duv = _MainTex_TexelSize.xyxy * float4(0.5, 0.5, -0.5, 0); | |
half4 acc; | |
acc = tex2D(_MainTex, input.uv - duv.xy); | |
acc += tex2D(_MainTex, input.uv - duv.zy); | |
acc += tex2D(_MainTex, input.uv + duv.zy); | |
acc += tex2D(_MainTex, input.uv + duv.xy); | |
return acc * 0.25f; | |
} | |
#endif |
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
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Rendering; | |
[ExecuteAlways] | |
[ImageEffectAllowedInSceneView] | |
[RequireComponent(typeof(Camera))] | |
public class HBAO : MonoBehaviour | |
{ | |
public enum HBAO_OutPass | |
{ | |
AO, | |
Blurred, | |
Combined | |
} | |
public enum HBAO_Resolution | |
{ | |
Full = 1, | |
Half = 2, | |
Quarter = 4 | |
} | |
[Header("HBAO Properties")] [SerializeField] | |
private HBAO_Resolution resolution = HBAO_Resolution.Half; | |
[SerializeField] [Range(0.1f, 10.0f)] private float AOStrength; | |
[SerializeField] [Range(0.01f,0.5f)] private float MaxRadiusInUV; | |
[SerializeField] [Range(0.1f,1.0f)] private float AOBias; | |
private Material HBAOMaterial_ = null; | |
private CommandBuffer HBAOcmd_ = null; | |
private Camera RenderCamera_ = null; | |
[SerializeField] private HBAO_OutPass HBAODebug = HBAO_OutPass.Combined; | |
public Material HBAOMaterial | |
{ | |
get | |
{ | |
if (!HBAOMaterial_) | |
{ | |
HBAOMaterial_ = new Material(Shader.Find("Hidden/HBAO")); | |
} | |
return HBAOMaterial_; | |
} | |
} | |
public Camera RenderCamera | |
{ | |
get | |
{ | |
if (RenderCamera_) return RenderCamera_; | |
RenderCamera_ = GetComponent<Camera>(); | |
return RenderCamera_; | |
} | |
} | |
public CommandBuffer HBAOcmd | |
{ | |
get | |
{ | |
if (HBAOcmd_ != null) return HBAOcmd_; | |
HBAOcmd_ = new CommandBuffer(); | |
HBAOcmd_.name = "HBAOCommandBuffer"; | |
return HBAOcmd_; | |
} | |
} | |
private static class ShaderSheets | |
{ | |
public static int AOStrength; | |
public static int AORadius; | |
public static int AOBias; | |
public static int InvTextureSize; | |
static ShaderSheets() | |
{ | |
AOStrength = Shader.PropertyToID("_AOStrength"); | |
AORadius = Shader.PropertyToID("_AORadius"); | |
AOBias = Shader.PropertyToID("_AOBias"); | |
InvTextureSize = Shader.PropertyToID("_InvTextureSize"); | |
} | |
} | |
//basic logic | |
private void OnEnable() | |
{ | |
RenderCamera.AddCommandBuffer(CameraEvent.BeforeImageEffectsOpaque,HBAOcmd); | |
} | |
private void OnDisable() | |
{ | |
RenderCamera.RemoveCommandBuffer(CameraEvent.BeforeImageEffectsOpaque,HBAOcmd); | |
} | |
//when the camera is removed | |
private void OnDestroy() | |
{ | |
HBAOcmd ? .Dispose(); | |
} | |
private void OnPreRender() | |
{ | |
UpdateVariable(); | |
RenderHBAO(); | |
} | |
private void UpdateVariable() | |
{ | |
HBAOMaterial.SetFloat(ShaderSheets.AOStrength,AOStrength); | |
HBAOMaterial.SetFloat(ShaderSheets.AOBias,AOBias); | |
float tanHalfFovY = Mathf.Tan(RenderCamera.fieldOfView * 0.5f * Mathf.Deg2Rad); | |
// AORadius决定了最大的步进范围,步进范围与viewZ成反比,viewZ越大步进范围越小 | |
float MaxRadius= MaxRadiusInUV * (RenderCamera.pixelHeight / (int)resolution) / tanHalfFovY; | |
HBAOMaterial.SetFloat(ShaderSheets.AORadius, MaxRadius); | |
// HBAOMaterial.SetMatrix("_WorldToViewMatrix", RenderCamera.worldToCameraMatrix); | |
} | |
private void RenderHBAO() | |
{ | |
HBAOcmd.Clear(); | |
Vector2Int wh = new Vector2Int(RenderCamera.pixelWidth, RenderCamera.pixelHeight); | |
HBAOMaterial.SetVector(ShaderSheets.InvTextureSize, new Vector2( | |
(float)resolution / wh[0], | |
(float)resolution / wh[1] | |
)); | |
RenderTexture color = RenderTexture.GetTemporary(wh[0], wh[1]); | |
HBAOcmd.Blit(BuiltinRenderTextureType.CurrentActive,color); | |
RenderTexture tmp1 = RenderTexture.GetTemporary(wh[0]/ (int)resolution , wh[1] / (int)resolution); | |
HBAOcmd.Blit(color,tmp1,HBAOMaterial,0); | |
RenderTexture blurTex = RenderTexture.GetTemporary(wh[0] / (int)resolution, wh[1] / (int)resolution); | |
RenderTexture blur2Tex = RenderTexture.GetTemporary(wh[0] / (int)resolution, wh[1] / (int)resolution); | |
HBAOcmd.Blit(tmp1,blurTex,HBAOMaterial,2); | |
HBAOcmd.Blit(blurTex,blur2Tex,HBAOMaterial,3); | |
if (HBAODebug == HBAO_OutPass.Combined) | |
{ | |
RenderTexture tmp2 = RenderTexture.GetTemporary(wh[0], wh[1]); | |
HBAOcmd.SetGlobalTexture("_AOTex",blur2Tex); | |
HBAOcmd.Blit(color,BuiltinRenderTextureType.CameraTarget,HBAOMaterial,1); | |
RenderTexture.ReleaseTemporary(tmp2); | |
} | |
else if(HBAODebug == HBAO_OutPass.AO) | |
{ | |
HBAOcmd.Blit(tmp1, BuiltinRenderTextureType.CameraTarget); | |
} | |
else | |
{ | |
HBAOcmd.Blit(blur2Tex, BuiltinRenderTextureType.CameraTarget); | |
} | |
RenderTexture.ReleaseTemporary(tmp1); | |
RenderTexture.ReleaseTemporary(blurTex); | |
RenderTexture.ReleaseTemporary(blur2Tex); | |
RenderTexture.ReleaseTemporary(color); | |
} | |
} |
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 "Hidden/HBAO" | |
{ | |
Properties | |
{ | |
_MainTex ("Texture", 2D) = "white" {} | |
} | |
SubShader | |
{ | |
// No culling or depth | |
Cull Off ZWrite Off ZTest Always | |
Pass | |
{ | |
CGPROGRAM | |
#pragma enable_d3d11_debug_symbols | |
#pragma vertex HBAO_vert | |
#pragma fragment HBAO_frag | |
#include "HBAO.cginc" | |
ENDCG | |
} | |
Pass | |
{ | |
CGPROGRAM | |
#pragma enable_d3d11_debug_symbols | |
#pragma vertex HBAO_vert | |
#pragma fragment HBAO_merge_frag | |
#include "HBAO.cginc" | |
ENDCG | |
} | |
Pass | |
{ | |
CGPROGRAM | |
#pragma enable_d3d11_debug_symbols | |
#pragma vertex HBAO_vert | |
#pragma fragment horizontal_blur | |
#include "HBAO.cginc" | |
ENDCG | |
} | |
Pass | |
{ | |
CGPROGRAM | |
#pragma enable_d3d11_debug_symbols | |
#pragma vertex HBAO_vert | |
#pragma fragment vertical_blur | |
#include "HBAO.cginc" | |
ENDCG | |
} | |
Pass | |
{ | |
CGPROGRAM | |
#pragma enable_d3d11_debug_symbols | |
#pragma vertex HBAO_vert | |
#pragma fragment tap4blur | |
#include "HBAO.cginc" | |
ENDCG | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Reference:
https://github.com/shadylyf321/HBAO
https://github.com/scanberg/hbao