Hey guys, back again with some more basic stuff. I need help getting a bloom curve, from the presentation HDR The Bungie Way by Chris Tchou. I dont like the way my bloom looks in its current state. I may look at some different techniques for bloom because currently there aren’t many that looks good in my opinion besides the few listed below. I’ve already implemented Kawase’s Bloom Filter, now i’m trying out this one. But the key component I don’t understand how to achieve is the bloom curve. I’ve uploaded just the slides, however some of the slides are missing pictures which hinder slightly my ability to understand. But a few of the pictures can also be found below, along with the current code I have. So what I ask is that someone help me with implementing a bloom curve to the HDR Lighting from [Bungie06], that will allow me to have bloom that looks gorgeous :). (Bloom Curve starts at slide 64).
I’ve extended this post to ask for help with other post processing matters on how to achieve better bloom/glare/ghosting effects with any tips or slides that you guys may have found. I wish to improve the quality of my lighting in games as well by making the lighting more “physically accurate”.
Bloom Curve (Taken from Bungie’s slides, but missing in the powerpoint, only found on the web):
Bloom Curve:
No Bloom Curve:
Examples of bloom/flares that I like and want to acheive something like:
I know I need some ghost samples for my bloom to also make it look “prettier”:
And I need streaking, I already have streaking but Its not as good as these other implementations like Yebis 2 or iCEnhancer the GTAIV ENB Series mod. Here is my implementation of streaking:
Lastly I want to improve my lighting , like Cody has done here, with Physically accurate sunlight and ambient colors:
Current Code:
Shader:
Shader "Custom/HDRLighting2"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_BloomTex ("Base (RGB)", 2D) = "white" {}
}
CGINCLUDE
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform sampler2D _OrignMaterial;
uniform sampler2D _BloomTex;
uniform sampler2D _BlurTex;
uniform sampler2D _Curve;
uniform float4 _BloomColor;
uniform float _BlurSize;
uniform float _RangeScale;
uniform float _BloomIntensity;
uniform float _BloomThreshold;
float4 offsets;
struct v2f
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float4 uv01 : TEXCOORD1;
float4 uv23 : TEXCOORD2;
float4 uv45 : TEXCOORD3;
};
v2f vert (appdata_img v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.xy = v.texcoord.xy;
o.uv01 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1);
o.uv23 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 2.0;
o.uv45 = v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 3.0;
return o;
}
float BloomCurve(float fBright)
{
fBright = max(tex2D(_Curve, float2(_RangeScale, 0.5)).x, 2.00f);
return fBright;
}
float4 Tonemap(v2f_img i) : COLOR
{
float4 vColor = tex2D(_MainTex, i.uv);
vColor.rgb *= 5.199f;
vColor.rgb = vColor.rgb / (1.0f + vColor.rgb);
vColor.rgb = pow(vColor.rgb, float3(1.0f / 0.550f, 1.0f / 0.552f, 1.0f / 0.549f));
vColor.rgb *= 1.10f;
return float4(vColor.rgb, 1.0f);
}
float4 Threshold(v2f_img i) : COLOR
{
float4 vSample = tex2D(_MainTex, i.uv);
return saturate((vSample - _BloomThreshold) / (1 - _BloomThreshold));
}
float4 Downsample(v2f_img i) : COLOR
{
float2 InputSize = float2( 1.0f / 1280.0f, 1.0f / 720.0f);
float4 vSample = tex2D(_MainTex, i.uv);
vSample += tex2D(_MainTex, i.uv+InputSize * float2(1.0f, 1.0f));
vSample += tex2D(_MainTex, i.uv+InputSize * float2(-1.0f, 1.0f));
vSample += tex2D(_MainTex, i.uv+InputSize * float2(1.0f, -1.0f));
vSample += tex2D(_MainTex, i.uv+InputSize * float2(-1.0f, -1.0f));
vSample /= 4.0f;
return vSample;
}
float4 Bloom(v2f_img i) : COLOR
{
float4 vSample = tex2D(_MainTex, i.uv);
float fIntensity = max(dot(vSample.rgb, float3(0.3f, 0.3f, 0.3f)), 0.0001f);
//_BloomIntensity = BloomCurve(fIntensity);
float3 fBloomColor = vSample.rgb * float3(_BloomIntensity, _BloomIntensity, _BloomIntensity) / float3(fIntensity, fIntensity, fIntensity);
return float4(fBloomColor, 1.0f);
}
float4 AccumBlur(v2f_img i) : COLOR
{
float4 vBloom = tex2D(_BloomTex, i.uv);
float4 vBlur = tex2D(_BlurTex, i.uv);
float4 vFinal = vBloom + vBlur;
return vFinal;
}
float4 Blur(v2f i) : COLOR
{
float4 color = float4 (0,0,0,0);
color += 0.40 * tex2D (_MainTex, i.uv);
color += 0.15 * tex2D (_MainTex, i.uv01.xy);
color += 0.15 * tex2D (_MainTex, i.uv01.zw);
color += 0.10 * tex2D (_MainTex, i.uv23.xy);
color += 0.10 * tex2D (_MainTex, i.uv23.zw);
color += 0.05 * tex2D (_MainTex, i.uv45.xy);
color += 0.05 * tex2D (_MainTex, i.uv45.zw);
return color;
}
float4 Composite(v2f_img i) : COLOR
{
float4 vSample = tex2D(_OrignMaterial, i.uv);
float4 vBloom = tex2D(_BloomTex, i.uv);
vBloom *= _BloomColor;
float4 vFinal = vBloom + vSample;
return vFinal;
}
ENDCG
Subshader {
ZTest Off
Cull Off
ZWrite Off
Fog { Mode off }
//Pass 0: Threshold
Pass
{
Name "Threshold"
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vert_img
#pragma fragment Threshold
ENDCG
}
//Pass 1: Downsample
Pass
{
Name "Downsample"
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vert_img
#pragma fragment Downsample
ENDCG
}
//Pass 2: Bloom
Pass
{
Name "Bloom"
CGPROGRAM
#pragma vertex vert_img
#pragma fragment Bloom
ENDCG
}
//Pass 3: AccumBlur
Pass
{
Name "AccumBlur"
CGPROGRAM
#pragma vertex vert_img
#pragma fragment AccumBlur
ENDCG
}
//Pass 4: Blur
Pass
{
Name "Blur"
CGPROGRAM
#pragma vertex vert
#pragma fragment Blur
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
//Pass 5: Tonemap
Pass
{
Name "Tonemap"
CGPROGRAM
#pragma vertex vert_img
#pragma fragment Tonemap
ENDCG
}
//Pass 6: Composite
Pass
{
Name "Composite"
CGPROGRAM
#pragma vertex vert_img
#pragma fragment Composite
ENDCG
}
}
Fallback off
}
C# (CPU code)
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class HDRLighting2 : MonoBehaviour {
#region Variables
public Shader HDRLightingShader;
public float BloomAmount = 1.0f;
public float BloomThreshold = 1.0f;
public Color BloomColor;
public float Blur = 1.0f;
private Material HDRLightingMaterial;
private RenderTexture BrightPass;
private RenderTexture DownSample_0;
private RenderTexture DownSample_1;
private RenderTexture DownSample_2;
private RenderTexture DownSample_3;
private RenderTexture HDRBloom;
private RenderTexture HDRBlur;
private RenderTexture LDRTonemap;
private RenderTexture UpSample_0;
private RenderTexture UpSample_1;
private RenderTexture UpSample_2;
private RenderTexture UpSample_3;
private RenderTexture Compose;
private RenderTextureFormat HDR = RenderTextureFormat.ARGBHalf;
private RenderTextureFormat LDR = RenderTextureFormat.ARGB32;
#endregion
#region Properties
Material material
{
get
{
if(HDRLightingMaterial == null)
{
HDRLightingMaterial = new Material(HDRLightingShader);
HDRLightingMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return HDRLightingMaterial;
}
}
#endregion
// Use this for initialization
void Start ()
{
if(!SystemInfo.supportsImageEffects)
{
enabled = false;
return;
}
//HDRLightingShader = Shader.Find("HDRLighting2");
}
void SetConstants()
{
material.SetFloat("_BloomIntensity", BloomAmount);
material.SetFloat("_BloomThreshold", BloomThreshold);
material.SetColor("_BloomColor", BloomColor);
}
void BrightPassFilter(RenderTexture source)
{
BrightPass = RenderTexture.GetTemporary(source.width, source.height, 0, HDR);
Graphics.Blit(source, BrightPass, material, 0);
}
void Downsample(RenderTexture source)
{
DownSample_0 = RenderTexture.GetTemporary(source.width / 2, source.height / 2, 0, HDR);
DownSample_1 = RenderTexture.GetTemporary(source.width / 4, source.height / 4, 0, HDR);
DownSample_2 = RenderTexture.GetTemporary(source.width / 8, source.height / 8, 0, HDR);
DownSample_3 = RenderTexture.GetTemporary(source.width / 16, source.height / 16, 0, HDR);
Graphics.Blit(BrightPass, DownSample_0, material, 1);
RenderTexture.ReleaseTemporary(BrightPass);
Graphics.Blit(DownSample_0, DownSample_1, material, 1);
RenderTexture.ReleaseTemporary(DownSample_0);
Graphics.Blit(DownSample_1, DownSample_2, material, 1);
RenderTexture.ReleaseTemporary(DownSample_1);
Graphics.Blit(DownSample_2, DownSample_3, material, 1);
RenderTexture.ReleaseTemporary(DownSample_2);
}
void Bloom(RenderTexture source)
{
HDRBloom = RenderTexture.GetTemporary(source.width / 16, source.height / 16, 0, HDR);
Graphics.Blit(DownSample_3, HDRBloom, material, 2);
RenderTexture.ReleaseTemporary(DownSample_3);
}
void AccumulateBlur(RenderTexture source)
{
HDRBlur = RenderTexture.GetTemporary(source.width / 16, source.height / 16, 0, HDR);
float WidthOverHeight = (source.width) / (source.height);
float OneOverBase = 1.0f / 512.0f;
//Seperable Blur (Unitys approach)
for(int i = 0; i < 2; i++)
{
material.SetVector ("offsets", new Vector4 (0.0f, Blur * OneOverBase, 0.0f, 0.0f));
Graphics.Blit (HDRBloom, HDRBlur, material, 4);
material.SetVector ("offsets", new Vector4 (Blur * OneOverBase / WidthOverHeight, 0.0f, 0.0f, 0.0f));
Graphics.Blit (HDRBlur, HDRBloom, material, 4);
}
UpSample_0 = RenderTexture.GetTemporary(source.width / 16, source.height / 16, 0, HDR);
UpSample_1 = RenderTexture.GetTemporary(source.width / 8, source.height / 8, 0, HDR);
UpSample_2 = RenderTexture.GetTemporary(source.width / 4, source.height / 4, 0, HDR);
UpSample_3 = RenderTexture.GetTemporary(source.width / 2, source.height / 2, 0, HDR);
Graphics.Blit(HDRBloom, UpSample_0, material, 3);
RenderTexture.ReleaseTemporary(HDRBloom);
material.SetTexture("_BlurTex", HDRBlur);
for(int i = 0; i < 2; i++)
{
material.SetVector ("offsets", new Vector4 (0.0f, Blur * OneOverBase, 0.0f, 0.0f));
Graphics.Blit (UpSample_0, HDRBlur, material, 4);
material.SetVector ("offsets", new Vector4 (Blur * OneOverBase / WidthOverHeight, 0.0f, 0.0f, 0.0f));
Graphics.Blit (HDRBlur, UpSample_0, material, 4);
}
Graphics.Blit(UpSample_0, UpSample_1, material, 3);
RenderTexture.ReleaseTemporary(UpSample_0);
for(int i = 0; i < 2; i++)
{
material.SetVector ("offsets", new Vector4 (0.0f, Blur * OneOverBase, 0.0f, 0.0f));
Graphics.Blit (UpSample_1, HDRBlur, material, 4);
material.SetVector ("offsets", new Vector4 (Blur * OneOverBase / WidthOverHeight, 0.0f, 0.0f, 0.0f));
Graphics.Blit (HDRBlur, UpSample_1, material, 4);
}
Graphics.Blit(UpSample_1, UpSample_2, material, 3);
RenderTexture.ReleaseTemporary(UpSample_1);
for(int i = 0; i < 2; i++)
{
material.SetVector ("offsets", new Vector4 (0.0f, Blur * OneOverBase, 0.0f, 0.0f));
Graphics.Blit (UpSample_2, HDRBlur, material, 4);
material.SetVector ("offsets", new Vector4 (Blur * OneOverBase / WidthOverHeight, 0.0f, 0.0f, 0.0f));
Graphics.Blit (HDRBlur, UpSample_2, material, 4);
}
RenderTexture.ReleaseTemporary(HDRBlur);
Graphics.Blit(UpSample_2, UpSample_3, material, 3);
RenderTexture.ReleaseTemporary(UpSample_2);
}
void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture)
{
if(HDRLightingShader != null)
{
SetConstants();
BrightPassFilter(sourceTexture);
Downsample(sourceTexture);
Bloom(sourceTexture);
AccumulateBlur(sourceTexture);
Compose = RenderTexture.GetTemporary(sourceTexture.width, sourceTexture.height, 0, LDR);
material.SetTexture("_BloomTex", UpSample_3);
material.SetTexture("_OrignMaterial", sourceTexture);
Graphics.Blit(UpSample_3, Compose, material, 6);
RenderTexture.ReleaseTemporary(UpSample_3);
Graphics.Blit(Compose, destTexture, material, 5);
RenderTexture.ReleaseTemporary(Compose);
}
}
// Update is called once per frame
void Update ()
{
BloomAmount = Mathf.Clamp(BloomAmount, 0.0f, 5.0f);
BloomThreshold = Mathf.Clamp(BloomThreshold, 0.0f, 1.0f);
}
void OnDisable ()
{
if(HDRLightingMaterial)
{
DestroyImmediate(HDRLightingMaterial);
}
}
}
[Bungie06] Dropbox - Error - Simplify your life
Thanks for your help guys, I will be back soon with help on auto-exposure without reinhard’s tonemap operator, just a pure custom auto-exposure.