这次要使用到顶点和片段着色器。同样是制作屏幕后处理效果。
下面是一开始的效果,只是一个循环播放的粒子系统。
我们首先要对源图像的亮部区域进行提亮,然后进行模糊添加光芒。然后进行混合,最后进行一次合成
我们需要创建一些临时纹理来存储渲染过程的中间阶段,之后先通过第一个pass将原图像提亮,下面是关键代码, Graphics.Blit(source, brightnessTex, material, 1)这个函数的就是通过material的第二个pass对图像进行处理,
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
RenderTexture brightnessTex = RenderTexture.GetTemporary(source.width / divide, source.height / divide, source.depth, source.format);
RenderTexture blurredTex1 = RenderTexture.GetTemporary(brightnessTex.descriptor);
RenderTexture blurredTex2 = RenderTexture.GetTemporary(brightnessTex.descriptor);
RenderTexture compositeTex = RenderTexture.GetTemporary(brightnessTex.descriptor);
material.SetVector(brightnessSettingsID, new Vector3(threshold, intensity, attenuation));
// 进行渲染操作
Graphics.Blit(source, brightnessTex, material, 1);
Graphics.Blit(brightnessTex, destination, material, 0);
//Graphics.Blit(source, destination, material, 0);
// 释放临时RenderTextures
RenderTexture.ReleaseTemporary(brightnessTex);
RenderTexture.ReleaseTemporary(blurredTex1);
RenderTexture.ReleaseTemporary(blurredTex2);
RenderTexture.ReleaseTemporary(compositeTex);
}
下面是shader代码
fixed4 frag(v2f_img input) : SV_Target
{
float4 color = tex2D(_MainTex,input.uv ) ;
return max(color - BRIGHTNESS_THRESHOLD,0)* INTENSITY;
}
然后下一步就是为星星添加光芒条纹。
要以特定角度模糊上一步的图像,然后迭代多次
创建一个新的pass,_MainTex_TexelSize.xy就是图像宽度的倒数。计算颜色之后每次对uv进行偏移并对颜色进行衰减
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
int _Iteration;
float2 _Offset;
struct v2f_starglow {
float4 pos : SV_POSITION; // 变换后的屏幕空间坐标
half2 uv : TEXCOORD0; // 纹理坐标
half power : TEXCOORD1;
half2 offset : TEXCOORD2; // 偏移量
};
v2f_starglow vert(appdata_img v)
{
v2f_starglow o;
// 计算顶点位置
o.pos = UnityObjectToClipPos(v.vertex);
// 传递纹理坐标
o.uv = v.texcoord;
o.power = pow(4.0, _Iteration - 1.0);
// 计算偏移量
o.offset = _MainTex_TexelSize.xy * _Offset * o.power;
return o;
}
float4 frag(v2f_starglow input) : SV_Target
{
half4 color = half4(0, 0, 0, 0); // 初始化颜色为黑色
half2 uv = input.uv;
// 遍历4个样本
for (int j = 0; j < 4; j++) {
// 采样纹理并应用衰减
color += saturate(tex2D(_MainTex, uv) * pow(ATTENUATION, input.power * j));
// 更新纹理坐标
uv += input.offset;
}
return color;
}
ENDCG
}
C#代码:
float angle = 360f / numOfStreaks;
for (int x = 1; x <= numOfStreaks; x++)
{
// 计算每个条纹的偏移量
Vector2 offset = (Quaternion.AngleAxis(angle * x + angleOfStreak, Vector3.forward) * Vector2.down).normalized;
// 设置材质的偏移量和迭代次数
material.SetVector(offsetID, offset);
material.SetInt(iterationID, 1);
// 使用材质进行图像处理
Graphics.Blit(brightnessTex, blurredTex1, material, 2);
for (int i = 2; i <= iteration; i++)
{
// 设置当前迭代次数
material.SetInt(iterationID, i);
// 进行图像处理,将 blurredTex1 处理并输出到 blurredTex2
Graphics.Blit(blurredTex1, blurredTex2, material, 2);
// 交换纹理,准备下一次迭代
RenderTexture temp = blurredTex1;
blurredTex1 = blurredTex2;
blurredTex2 = temp;
}
}
然后要进行一次混合
// STEP:3
Pass
{
// 设置混合模式
Blend OneMinusDstColor One
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
// 定义片元着色器
fixed4 frag(v2f_img input) : SV_Target
{
// 从 _MainTex 采样颜色并返回
return tex2D(_MainTex, input.uv);
}
ENDCG
}
最后将源图像与上一步中得到的图像进行叠加
最终效果:
完整代码:
starglow.shader
Shader "ImageEffect/StarGlow"
{
Properties
{
[HideInInspector]
_MainTex("Texture", 2D) = "white" {}
_BrightnessSettings("(Threshold, Intensity, Attenuation, -)", Vector) = (0.8, 1.0, 0.95, 0.0)
}
SubShader
{
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
float4 _BrightnessSettings;
#define BRIGHTNESS_THRESHOLD _BrightnessSettings.x
#define INTENSITY _BrightnessSettings.y
#define ATTENUATION _BrightnessSettings.z
ENDCG
// STEP:0
// Debug.
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
fixed4 frag(v2f_img input) : SV_Target
{
return tex2D(_MainTex, input.uv);
}
ENDCG
}
// STEP:1
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
fixed4 frag(v2f_img input) : SV_Target
{
float4 color = tex2D(_MainTex,input.uv ) ;
return max(color - BRIGHTNESS_THRESHOLD,0)* INTENSITY;
}
ENDCG
}
// STEP:2
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
int _Iteration;
float2 _Offset;
struct v2f_starglow {
float4 pos : SV_POSITION; // 变换后的屏幕空间坐标
half2 uv : TEXCOORD0; // 纹理坐标
half power : TEXCOORD1;
half2 offset : TEXCOORD2; // 偏移量
};
v2f_starglow vert(appdata_img v)
{
v2f_starglow o;
// 计算顶点位置
o.pos = UnityObjectToClipPos(v.vertex);
// 传递纹理坐标
o.uv = v.texcoord;
o.power = pow(4.0, _Iteration - 1.0);
// 计算偏移量
o.offset = _MainTex_TexelSize.xy * _Offset * o.power;
return o;
}
float4 frag(v2f_starglow input) : SV_Target
{
half4 color = half4(0, 0, 0, 0); // 初始化颜色为黑色
half2 uv = input.uv;
// 遍历4个样本
for (int j = 0; j < 4; j++) {
// 采样纹理并应用衰减
color += saturate(tex2D(_MainTex, uv) * pow(ATTENUATION, input.power * j));
// 更新纹理坐标
uv += input.offset;
}
return color;
}
ENDCG
}
// STEP:3
Pass
{
// 设置混合模式
Blend OneMinusDstColor One
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
// 定义片元着色器
fixed4 frag(v2f_img input) : SV_Target
{
// 从 _MainTex 采样颜色并返回
return tex2D(_MainTex, input.uv);
}
ENDCG
}
// Step 4 Pass
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
sampler2D _CompositeTex;
float4 _CompositeColor;
fixed4 frag(v2f_img input) : SV_Target
{
// 从 _MainTex 采样颜色
float4 mainColor = tex2D(_MainTex, input.uv);
// 从 _CompositeTex 采样颜色
float4 compositeColor = tex2D(_CompositeTex, input.uv);
// 计算 compositeColor 的灰度,并乘以 _CompositeColor
compositeColor.rgb = (compositeColor.r + compositeColor.g + compositeColor.b) * 0.3333 * _CompositeColor.rgb;
// 将 mainColor 和 compositeColor 叠加,并限制在 [0,1] 范围
return saturate(mainColor + compositeColor);
}
ENDCG
}
}
}
starglow.cs
using System.Collections.Generic;
using UnityEngine;
public class StarGlow : MonoBehaviour
{
#region Field
[Range(0, 1)]
public float threshold = 1;
[Range(0, 10)]
public float intensity = 1;
[Range(1, 20)]
public int divide = 3;
[Range(1, 5)]
public int iteration = 5;
[Range(0, 1)]
public float attenuation = 1;
[Range(0, 360)]
public float angleOfStreak = 0;
[Range(1, 16)]
public int numOfStreaks = 4;
public Material material;
public Color color = Color.white;
private int compositeTexID = 0;
private int compositeColorID = 0;
private int brightnessSettingsID = 0;
private int iterationID = 0;
private int offsetID = 0;
#endregion Field
#region Method
void Start()
{
compositeTexID = Shader.PropertyToID("_CompositeTex");
compositeColorID = Shader.PropertyToID("_CompositeColor");
brightnessSettingsID = Shader.PropertyToID("_BrightnessSettings");
iterationID = Shader.PropertyToID("_Iteration");
offsetID = Shader.PropertyToID("_Offset");
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
RenderTexture brightnessTex = RenderTexture.GetTemporary(source.width / divide, source.height / divide, source.depth, source.format);
RenderTexture blurredTex1 = RenderTexture.GetTemporary(brightnessTex.descriptor);
RenderTexture blurredTex2 = RenderTexture.GetTemporary(brightnessTex.descriptor);
RenderTexture compositeTex = RenderTexture.GetTemporary(brightnessTex.descriptor);
material.SetVector(brightnessSettingsID, new Vector3(threshold, intensity, attenuation));
// 进行渲染操作
Graphics.Blit(source, brightnessTex, material, 1);
float angle = 360f / numOfStreaks;
for (int x = 1; x <= numOfStreaks; x++)
{
// 计算每个条纹的偏移量
Vector2 offset = (Quaternion.AngleAxis(angle * x + angleOfStreak, Vector3.forward) * Vector2.down).normalized;
// 设置材质的偏移量和迭代次数
material.SetVector(offsetID, offset);
material.SetInt(iterationID, 1);
// 使用材质进行图像处理
Graphics.Blit(brightnessTex, blurredTex1, material, 2);
for (int i = 2; i <= iteration; i++)
{
// 设置当前迭代次数
material.SetInt(iterationID, i);
// 进行图像处理,将 blurredTex1 处理并输出到 blurredTex2
Graphics.Blit(blurredTex1, blurredTex2, material, 2);
// 交换纹理,准备下一次迭代
RenderTexture temp = blurredTex1;
blurredTex1 = blurredTex2;
blurredTex2 = temp;
}
Graphics.Blit(blurredTex1, compositeTex, material, 3);
}
material.SetColor(compositeColorID, color);
material.SetTexture(compositeTexID,compositeTex);
Graphics.Blit(source, destination,material,4);
//Graphics.Blit(source, destination, material, 0);
// 释放临时RenderTextures
RenderTexture.ReleaseTemporary(brightnessTex);
RenderTexture.ReleaseTemporary(blurredTex1);
RenderTexture.ReleaseTemporary(blurredTex2);
RenderTexture.ReleaseTemporary(compositeTex);
}
#endregion Method
}