一、需要提前了解的
这一章的例子非常的综合,可以说是知识点广而全,如果能独立在 Unity 中实现一个不错的泛光效果,那么 UnityShader 就算作入门成功!本章没有新的东西
曾经在 OpenGL 的教程中,就已经实现过泛光效果了:
- OpenGL基础51:泛光
- OpenGL基础35:帧缓冲(下)之简单图像处理:里面提到了一些图像处理的算子和卷积
- OpenGL基础50:HDR:还是看看
明白了泛光的原理后,就是 UnityShader 的基础部分:
- UnityShader24:最简单的屏幕后处理例子:泛光必然也是一种后处理手段
- UnityShader20.1:CommandBuffer初见(下):又是这一章!在这里已经在 Unity 中实现过了高斯模糊
到此一游
二、着色器部分
沿用这一章的高斯消元着色器,不过这次做一些改动,将顶点着色器部分分成两个,分别为横向和纵向,并且定义它们的名字:GAUSSBLURRED_HORIZONTAL 和 GAUSSBLURRED_VERTICAL
Shader "Jaihk662/GaussBlurred1"
{
Properties
{
_MainTex ("Base(RGB)", 2D) = "" {}
}
//和之前常用的 CGPROGRAM 不同,在 CGINCLUDE 和 ENDCG 范围内插入的 shader 代码会被插入到所有 Pass 中
CGINCLUDE
#include "UnityCG.cginc"
float offsets;
sampler2D _MainTex;
struct vert2frag
{
float4 pos: SV_POSITION;
float4 uv[4]: TEXCOORD0;
};
vert2frag vertVertical(appdata_img v)
{
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv[0] = v.texcoord.xyxy;
o.uv[1] = o.uv[0] + offsets * float4(0, 1, 0, -1);
o.uv[2] = o.uv[0] + offsets * float4(0, 2, 0, -2);
o.uv[3] = o.uv[0] + offsets * float4(0, 3, 0, -3);
return o;
}
vert2frag vertHorizontal(appdata_img v)
{
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv[0] = v.texcoord.xyxy;
o.uv[1] = o.uv[0] + offsets * float4(1, 0, -1, 0);
o.uv[2] = o.uv[0] + offsets * float4(2, 0, -2, 0);
o.uv[3] = o.uv[0] + offsets * float4(3, 0, -3, 0);
return o;
}
half4 frag(vert2frag i): SV_Target
{
//高斯模糊
float weight[4] = {0.4, 0.15, 0.1, 0.05};
half4 color = float4(0, 0, 0, 0);
color += weight[0] * tex2D(_MainTex, i.uv[0].xy);
for (int t = 1; t < 4; t++)
color += weight[t] * (tex2D(_MainTex, i.uv[t].xy) + tex2D(_MainTex, i.uv[t].zw));
return color;
}
ENDCG
Subshader
{
Pass
{
NAME "GAUSSBLURRED_HORIZONTAL"
ZTest Always Cull Off ZWrite Off
Fog { Mode off }
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vertHorizontal
#pragma fragment frag
ENDCG
}
Pass
{
NAME "GAUSSBLURRED_VERTICAL"
ZTest Always Cull Off ZWrite Off
Fog { Mode off }
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vertVertical
#pragma fragment frag
ENDCG
}
}
}
然后就是引用了高斯模糊,泛光的着色器:其中 Pass 包括:
- 提取亮色
- 高斯模糊 x 2
- 将模糊亮色纹理和原纹理合并
来来去去就是这些,没有什么新的东西
Shader "Jaihk662/Bloom"
{
Properties
{
_MainTex("Base (RGB)", 2D) = "white" {}
_BloomTex("BloomTex (RGB)", 2D) = "black" {} //提取的亮色贴图存到这里
_LuminanceThreshold("Luminance Threshold", Float) = 0.5 //亮度阈值,亮度超过这个值的片段被当作光源
_BlurSize("Blur Size", Float) = 1.0
}
//和之前常用的 CGPROGRAM 不同,在 CGINCLUDE 和 ENDCG 范围内插入的 shader 代码会被插入到所有 Pass 中
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _Bloom;
float _LuminanceThreshold;
float _BlurSize;
half4 _MainTex_TexelSize;
struct vert2frag
{
float4 pos: SV_POSITION;
half2 uv: TEXCOORD0;
};
//得到当前RGB值对应的灰度,也就是亮度
fixed luminance(fixed4 color)
{
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
//提取亮色顶点着色器
vert2frag vertExtractBright(appdata_img v)
{
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
//提取亮色顶点着色器
fixed4 fragExtractBright(vert2frag i): SV_Target
{
fixed4 color = tex2D(_MainTex, i.uv);
fixed val = clamp(luminance(color) - _LuminanceThreshold, 0.0, 1.0); //clamp(a, l, r):将a的值截取到l到r的范围内
//若当前片段非亮部,那么val值显然为0,最终返回结果为黑色
return color * val;
}
struct v2fBloom
{
float4 pos: SV_POSITION;
half4 uv: TEXCOORD0;
};
v2fBloom vertBloom(appdata_img v)
{
v2fBloom o;
o.pos = UnityObjectToClipPos (v.vertex);
o.uv.xy = v.texcoord;
o.uv.zw = v.texcoord;
//对亮色纹理进行平台差异化处理(怀疑可以用.cginc里面的方法)
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0.0)
o.uv.w = 1.0 - o.uv.w;
#endif
return o;
}
fixed4 fragBloom(v2fBloom i): SV_Target
{
return tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.zw);
}
ENDCG
Subshader
{
ZTest Always Cull Off ZWrite Off
Pass
{
CGPROGRAM
#pragma vertex vertExtractBright
#pragma fragment fragExtractBright
ENDCG
}
UsePass "Jaihk662/GaussBlurred1/GAUSSBLURRED_HORIZONTAL"
UsePass "Jaihk662/GaussBlurred1/GAUSSBLURRED_VERTICAL"
Pass
{
CGPROGRAM
#pragma vertex vertBloom
#pragma fragment fragBloom
ENDCG
}
}
FallBack Off
}
三、脚本部分
using UnityEngine;
using System.Collections;
public class Bloom: PostEffectsBase
{
public Shader shader;
private Material _material;
public Material material
{
get
{
_material = CheckShaderAndCreateMaterial(shader, _material);
return _material;
}
}
[Range(0, 4)]
//高斯模糊次数
public int iterations = 3;
//亮度阈值,亮度超过这个值的片段被当作光源,考虑了HDR
[Range(0.0f, 4.0f)]
public float luminanceThreshold = 0.6f;
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
material.SetFloat("_LuminanceThreshold", luminanceThreshold);
int rtW = src.width / 2;
int rtH = src.height / 2;
RenderTexture bufA = RenderTexture.GetTemporary(rtW, rtH, 0);
bufA.filterMode = FilterMode.Bilinear;
//调用第一个Pass提取亮色
Graphics.Blit(src, bufA, material, 0);
for (int i = 0; i < iterations; i++)
{
material.SetFloat("_BlurSize", 1.0f + i * 0.6f);
//高斯模糊
RenderTexture bufB = RenderTexture.GetTemporary(rtW, rtH, 0);
Graphics.Blit(bufA, bufB, material, 1);
RenderTexture.ReleaseTemporary(bufA);
bufA = bufB;
//高斯模糊
bufB = RenderTexture.GetTemporary(rtW, rtH, 0);
Graphics.Blit(bufA, bufB, material, 2);
RenderTexture.ReleaseTemporary(bufA);
bufA = bufB;
}
material.SetTexture("_Bloom", bufA);
//调用第4个Pass混合亮色和原图
Graphics.Blit(src, dest, material, 3);
RenderTexture.ReleaseTemporary(bufA);
}
else
{
Graphics.Blit(src, dest);
}
}
}