上一篇介绍了区域灰度化的原理与效果的简单实现,这篇实验继续上篇已完成的实验代码,做一个舞台追光的效果
介绍
舞台追光效果或许是最贴合区域灰度化在Shader上应用的例子,它的明暗变化正好能够衬托场景的整体氛围。
分析
上一篇的原理部分已经对区域灰度化的实现算法有过分析,这里不再过多阐述。我们主要对实现的效果进行说明,首先场景中新建了三个面片,分别是两堵墙与舞台地面,随着鼠标的移动,光圈也随之移动,下面进入实验部分(结尾会提供本篇实验的工程文件的下载地址)。
实验
打开上节实验中的Shader文件,在Properties处新增_MainColor与_ColorStrength两个属性:
Properties {
_MainTex ("Texture", 2D) = "white" {}
// 调整整体环境的颜色(包括光圈与舞台跟墙面)
_MainColor ("Main Color", Color) = (0.5,0.5,0.5,1.0)
_Position ("World Position", Vector) = (0,0,0,1)
// 只影响光圈区域的颜色强度
_ColorStrength("Color Strength", Range(1,4)) = 1
_Radius("Radius", Range(0,100)) = 3
_Softness("Softness", Range(0,100)) = 10
}
在frag函数中应用_MainColor与_ColorStrength属性:
half _ColorStrength;
fixed4 _MainColor;
fixed4 frag (v2f i) : SV_Target {
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv) * _MainColor;
// 灰度化(加权平均法)不清楚的朋友可以回头看Shader实验室:图片灰度化
fixed val = 0.299*col.r+0.578*col.g+0.114*col.b;
// 灰度化
fixed3 grayScale = fixed3(val, val, val);
// _Radius - distance的最小值为0
half d = saturate((_Radius - distance(_Position.xyz,i.w_p.xyz))/_Softness);
// 当Radius大于distance时,才处理灰度化,否则就为图片原来的颜色
col = lerp(fixed4(grayScale,1.0),col * _ColorStrength, d);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
当Main Color颜色偏暗,整体效果也会偏暗(包括光圈),当ColorStrength强度越高,光圈也会越亮,效果如下:
但是要实现光圈随着鼠标的移动而移动,只做到这些还远远不够,我们还需要新建一个C#来传递鼠标RaycastHit所处的坐标,并传递至该Shader中,并把Shader中的_Position,_Radius与_Softness移到该C#脚本中,现在Shader中的Properties中的代码如下:
Properties {
_MainTex ("Texture", 2D) = "white" {}
_MainColor ("Main Color", Color) = (0.5,0.5,0.5,1.0)
_ColorStrength("Color Strength", Range(1,8)) = 1
}
在CGPROGRAM中把_Position,_Radius,_Softness属性声明为uniform(告诉Shaderlab这三个属性值是从外部传入):
uniform float4 _Position;
uniform half _Radius;
uniform half _Softness;
新建的C#代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RayPosition : MonoBehaviour {
private Camera mainCamera;
private Ray ray;
private RaycastHit hit;
private Vector3 mousePos, hitPosition;
[Range(0.0f, 100.0f)]
public float radius = 11;
[Range(0.0f, 100.0f)]
public float softness = 4;
[Range(10.0f, 50.0f)]
public float moveSpeed = 15;
// Start is called before the first frame update
void Start() {
mainCamera = this.GetComponent();
}
// Update is called once per frame
void Update() {
mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
ray = mainCamera.ScreenPointToRay(mousePos);
// 通过raycast确认_Position的位置
if (Physics.Raycast(ray, out hit)) {
hitPosition = Vector3.MoveTowards(hitPosition, hit.point, moveSpeed * Time.deltaTime);
Shader.SetGlobalVector("_Position", new Vector4(hitPosition.x, hitPosition.y, hitPosition.z, 1));
}
Shader.SetGlobalFloat("_Radius", this.radius);
Shader.SetGlobalFloat("_Softness", this.softness);
}
}
我们使用Shader.SetGlobalFloat与Shader.SetGlobalVector对_Radius,_Softness以及_Position进行Shader赋值,通过这种方式可以不用去指定材质球,但是这种赋值方式会对使用相同Shader的不同材质传递相同的值,没办法针对特定材质赋予不同的值,要想特定材质赋值就需要使用Material.SetXXX。当然从性能上来讲Shader.SetGlobalXXX的方式会比Material.SetXXX的方式来的更快,但是当前实验正好需要对不同材质使用的相同Shader赋予相同的值,所以Shader.SetGlobalXXX当然是最好的选择。把该C#脚本挂载至Main Camera中,运行场景发现已经实现了鼠标移动,光圈也跟着移动的效果。
进行到这一步还没最终完成,我们需要做个小小的修饰,细心的朋友会发现文章开头图片中的舞台地面有Emission自发光的光圈效果。所以需要对舞台的图片进行ps处理。
反相处理
对比度处理
色阶处理
原理就是让图片有像素内容区域的地方更亮,不相关区域更暗。另存处理后的图片后新增一个材质作为舞台地面的专属材质球,Shader还是指定我们之前新建的Shader。此时我们已经有Emission的相关素材了,回到Shader中,对Properties新增Emission相关的图片属性。
Properties {
_MainTex ("Texture", 2D) = "white" {}
_MainColor ("Main Color", Color) = (0.5,0.5,0.5,1.0)
_ColorStrength("Color Strength", Range(1,8)) = 1
_EmissionTex ("Emission Texture", 2D) = "white" {}
_EmissionColor ("Emission Color", Color) = (0.5,0.5,0.5,1.0)
_EmissionStrength("Emission Strength", Range(1,8)) = 1
}
在struct v2f中新增emission相关uv寄存器
struct v2f {
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float4 w_p: TEXCOORD1;
float2 uv2: TEXCOORD2;
};
在vert函数新增代码,TRANSFORM_TEX是Unity内置的uv处理函数,内部原理是v.uv * _EmissionTex_ST.xy + _EmissionTex_ST.zw,这里o.uv2也可以直接等于v.uv,只不过_EmissionTex就没有办法调整它的Tiling与Offset参数值了。
o.uv2 = TRANSFORM_TEX(v.uv, _EmissionTex);
frag函数的处理。
half _EmissionStrength;
sampler2D _EmissionTex;
float4 _EmissionTex_ST;
fixed4 _EmissionColor;
fixed4 frag (v2f i) : SV_Target {
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv) * _MainColor;
// Emission贴图的颜色设置与颜色增强
fixed4 emissionCol = tex2D(_EmissionTex, i.uv2) * _EmissionColor * _EmissionStrength;
// 灰度化(加权平均法)不清楚的朋友可以回头看Shader实验室:图片灰度化
fixed val = 0.299*col.r+0.578*col.g+0.114*col.b;
// 灰度化
fixed3 grayScale = fixed3(val, val, val);
// _Radius - distance的最小值为0
half d = saturate((_Radius - distance(_Position.xyz,i.w_p.xyz))/_Softness);
// 当Radius大于distance时,才处理灰度化,否则就为图片原来的颜色
col = lerp(fixed4(grayScale,1.0),col * _ColorStrength, d);
// 还得加上Emission的颜色值
col += lerp(fixed4(grayScale,1.0),emissionCol, d)
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
最后效果。
本篇的源代码地址为 舞台灯光效果。
本篇就到这里了,如果觉得有用,就用微信扫描下面的二维码,关注我们吧!