写在前面
这个系列来源于书籍冯乐乐老师的《unity shader 入门精要》,
参考的代码库“https://github.com/candycat1992/Unity_Shaders_Book”
我自己跟着写了一遍代码,并在代码块里给shader代码加上了比较详细的注释
更为仔细的解释和unity shader原理和知识书里都有,本blog不做详细解释,推荐买书来看。
本项目的所有代码在https://github.com/takashiwangbh/Unity-shader-effect-reproduction/tree/main
介绍
屏幕后处理效果是游戏种实现屏幕特效的常见方法,是对游戏画面在渲染完成后进行的视觉调整,比如亮度、对比度、模糊、光晕等,提升画面质量和艺术效果。
要实现屏幕后处理的基础在于得到渲染后的屏幕图像,也就是抓取屏幕,而unity提供了一个方便的接口——OnRenderImage。这部分的shader都是挂在摄像机上,其实是通过摄像机去渲染我们想要的效果,而不是直接渲染物体,所以在unity的Scene界面看不到效果,在game界面就能看到最后的效果。
C# 脚本:控制和管理逻辑
- 职责:处理逻辑,设置参数,控制流程。
- 作用:
- 管理 Shader 的参数,例如亮度阈值、模糊半径等。
- 控制后处理效果的强度和行为,比如动态调整模糊层级。
- 处理渲染目标(
RenderTexture
)的创建和释放。
- 实现:
- 在 C# 中,通过
Material
将参数值传递给 Shader。 - 使用
Graphics.Blit
将图像从一个渲染目标复制到另一个,并应用 Shader。
- 在 C# 中,通过
Shader:具体的视觉效果实现
- 职责:定义像素的渲染规则,决定屏幕效果如何呈现。
- 作用:
- 操作每个像素的颜色和亮度,比如提取高亮部分。
- 执行模糊、合并等操作,创造特定的视觉效果。
- 实现 C# 脚本中配置的视觉逻辑。
总的来说就是C# 脚本负责控制逻辑和传递参数,Shader 负责实现具体的视觉效果,二者配合完成后处理渲染。
本期将会使用屏幕后处理效果实现一些图像处理的功能。
调整亮度,饱和度和对比度
给相机加上脚本
/*
这个脚本用于实现屏幕后处理效果中的亮度、饱和度和对比度调整。
它通过自定义的 Shader 来处理图像,并允许开发者在 Unity Inspector 中
调整亮度、饱和度和对比度的数值,从而实时影响画面效果。
*/
using UnityEngine;
using System.Collections;
public class BrightnessSaturationAndContrast : PostEffectsBase {
// 引用一个自定义 Shader,用于调整亮度、饱和度和对比度
public Shader briSatConShader;
// 基于 Shader 创建的材质,用于应用屏后处理效果
private Material briSatConMaterial;
// 延迟加载材质,当需要时自动检查 Shader 并创建材质
public Material material {
get {
// 检查 Shader 是否支持并创建材质
briSatConMaterial = CheckShaderAndCreateMaterial(briSatConShader, briSatConMaterial);
return briSatConMaterial;
}
}
// 亮度调整范围(从 0.0 到 3.0),默认值为 1.0
[Range(0.0f, 3.0f)]
public float brightness = 1.0f;
// 饱和度调整范围(从 0.0 到 3.0),默认值为 1.0
[Range(0.0f, 3.0f)]
public float saturation = 1.0f;
// 对比度调整范围(从 0.0 到 3.0),默认值为 1.0
[Range(0.0f, 3.0f)]
public float contrast = 1.0f;
// Unity 中的屏后处理方法,在摄像机渲染完成后调用
void OnRenderImage(RenderTexture src, RenderTexture dest) {
// 检查材质是否可用
if (material != null) {
// 将亮度、饱和度和对比度的值传递给 Shader
material.SetFloat("_Brightness", brightness);
material.SetFloat("_Saturation", saturation);
material.SetFloat("_Contrast", contrast);
// 使用材质将源纹理 (src) 渲染到目标纹理 (dest)
Graphics.Blit(src, dest, material);
} else {
// 如果材质不可用,直接将源纹理复制到目标纹理
Graphics.Blit(src, dest);
}
}
}
在相机的属性界面,在挂脚本的组件里引用shader
/*
这个 Shader 用于调整图像的亮度、饱和度和对比度。
开发者可以通过在 Unity Inspector 中设置对应的参数值,
实时改变画面效果,从而实现所需的图像调整功能。
*/
Shader "Basic properties of images" {
Properties {
// 定义 Shader 可调节的属性:主纹理、亮度、饱和度和对比度
_MainTex ("Base (RGB)", 2D) = "white" {
}
_Brightness ("Brightness", Float) = 1
_Saturation("Saturation", Float) = 1
_Contrast("Contrast", Float) = 1
}
SubShader {
Pass {
// 禁用深度测试和剔除,设置为始终渲染
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// 定义纹理和调节参数
sampler2D _MainTex;
half _Brightness;
half _Saturation;
half _Contrast;
// 顶点着色器输出结构
struct v2f {
float4 pos : SV_POSITION; // 顶点位置
half2 uv: TEXCOORD0; // 纹理坐标
};
// 顶点着色器:将顶点坐标转换为剪辑空间坐标,并传递纹理坐标
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
// 片段着色器:对图像像素应用亮度、饱和度和对比度调整
fixed4 frag(v2f i) : SV_Target {
// 读取纹理像素颜色
fixed4 renderTex = tex2D(_MainTex, i.uv);
// 调整亮度
fixed3 finalColor = renderTex.rgb * _Brightness;
// 调整饱和度
fixed luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b; // 计算亮度值
fixed3 luminanceColor = fixed3(luminance, luminance, luminance); // 构造灰度图
finalColor = lerp(luminanceColor, finalColor, _Saturation); // 根据饱和度插值
// 调整对比度
fixed3 avgColor = fixed3(0.5, 0.5, 0.5); // 基准值
finalColor = lerp(avgColor, finalColor, _Contrast); // 根据对比度插值
// 返回调整后的颜色,保留原始透明度
return fixed4(finalColor, renderTex.a);
}
ENDCG
}
}
// 禁用回退到默认 Shader
Fallback Off
}
效果展示
边缘检测
给相机加上脚本
/*
这个脚本用于实现屏幕后处理的边缘检测效果。
通过自定义 Shader,对图像的边缘进行检测,并允许用户调整边缘的显示方式,
如边缘透明度、边缘颜色和背景颜色。
*/
using UnityEngine;
using System.Collections;
public class EdgeDetection : PostEffectsBase {
// 引用用于边缘检测的 Shader
public Shader edgeDetectShader;
// 用于存储和应用 Shader 的材质
private Material edgeDetectMaterial = null;
// 延迟加载材质,确保 Shader 检查通过后创建材质
public Material material {
get {
edgeDetectMaterial = CheckShaderAndCreateMaterial(edgeDetectShader, edgeDetectMaterial);
return edgeDetectMaterial;
}
}
// 边缘显示的强度范围(0 表示完全显示背景,1 表示只显示边缘)
[