UnityShader25:在Unity中实现泛光

本文详细介绍了如何在Unity中利用Shader实现泛光效果,结合高斯模糊技术,从理论到实践,包括顶点着色器和片段着色器的编写,以及相关脚本的实现。通过提取亮部、多次高斯模糊和混合原图,最终达到全局泛光的视觉效果。
摘要由CSDN通过智能技术生成

 

一、需要提前了解的

这一章的例子非常的综合,可以说是知识点广而全,如果能独立在 Unity 中实现一个不错的泛光效果,那么 UnityShader 就算作入门成功!本章没有新的东西

曾经在 OpenGL 的教程中,就已经实现过泛光效果了:

明白了泛光的原理后,就是 UnityShader 的基础部分:

到此一游

 

二、着色器部分

沿用这一章的高斯消元着色器,不过这次做一些改动,将顶点着色器部分分成两个,分别为横向和纵向,并且定义它们的名字: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);
		}
	}
}
全局泛光效果,本质上是 HDR 的一种感官体现

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值