一、ShaderToy作品
如果你對 Shader 有一定的了解,那么你或多或少聽說過 shaderToy 這個網站,這個網站上有很多令人振奮的 shader 效果,而這些效果有可能只用了幾行代碼來實作,就如同畫家繪畫,在這里片段著色器就是畫筆,螢屏就是畫紙:

網站中對于任意一個作品,都提供了完整的 GLSL 片段著手器代碼,它們是通過在你的瀏覽器中運行 WebGl 來展現這些效果的,你也可以通過修改代碼,修改變數和輸入來直接在網頁上查看效果的變幻
把它搬運到 Unity 上并不難,由于它們都是在片段著色器中進行逐像素的繪制和處理,所以在 Unity 中,我們可以用螢屏后處理的方式來處理這些 Shader
二、在Unity中實作
C# 后處理腳本如下:
為了能提高某些高復雜度 Shader 的性能,所以可以指定畫布的解析度
using System;
using UnityEngine;
[ExecuteInEditMode]
public class ForShaderToy: PostEffectsBase
{
public int horizontal = 1920;
public int vertical = 1080;
public Shader shaderToy;
private Material _material;
public Material material
{
get
{
_material = CheckShaderAndCreateMaterial(shaderToy, _material);
return _material;
}
}
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
RenderTexture scaled = RenderTexture.GetTemporary(horizontal, vertical, 24);
Graphics.Blit(src, scaled, material);
Graphics.Blit(scaled, dest);
RenderTexture.ReleaseTemporary(scaled);
}
else
Graphics.Blit(src, dest);
}
}
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
//當在物體上添加該腳本時,Camera組件也會被自動被添加上去(如果沒有的話)
[RequireComponent(typeof(Camera))]
public class PostEffectsBase: MonoBehaviour
{
protected void Start()
{
//如果顯卡支持影像后期處理效果
if (!SystemInfo.supportsImageEffects)
{
//設定當前組件為關閉狀態
enabled = false;
}
}
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
{
//判斷當前著色器是否可在當前顯卡設備上使用,如果對應的著色器的所有Fallback都不可支持或者傳入的著色器就不存在,回傳null
if (shader == null || !shader.isSupported)
return null;
if (material && material.shader == shader)
return material;
else
{
material = new Material(shader);
//設定當前材質不會被保存在場景中
material.hideFlags = HideFlags.HideAndDontSave;
if (material)
return material;
else
return null;
}
}
搞定之后,就是將 ShaderToy 上的 Shader 搬運過來了,如果想要在 UnityShader 中使用 HLSL,那么就需要自己去實作 GLSL 到 HLSL 的轉換
這個可以參考 https://alastaira.wordpress.com/2015/08/07/unity-shadertoys-a-k-a-converting-glsl-shaders-to-cghlsl/ 以及 https://msdn.microsoft.com/en-GB/library/windows/apps/dn166865.aspx
沒問題了,接下來把上面的 C# 掛到攝像機下,組件指定 Shader 就好,一個例子如下:
//https://www.shadertoy.com/view/XlfGRj
//https://msdn.microsoft.com/en-GB/library/windows/apps/dn166865.aspx
Shader "ShaderToy/StarNest"
{
Properties
{
_Zoom("Zoom", Float) = 0.8 //縮放
_Speed("Speed", Float) = 0.01 //速度
_Volsteps("Volsteps", Int) = 20
_Tile("Tile", Float) = 0.85
_Iterations("Iterations", Int) = 17
_Formuparam("Formuparam", Float) = 0.53
_Brightness("Brightness", Float) = 0.0015
_Darkmatter("Darkmatter", Float) = 0.300
_Distfading("Distfading", Float) = 0.730
_Saturation("Saturation", Float) = 0.850
_StepSize("StepSize", Float) = 0.1
}
SubShader
{
PASS
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Zoom;
float _Speed;
int _Volsteps;
float _Tile;
float _Formuparam;
int _Iterations;
float _Brightness;
float _Darkmatter;
float _Distfading;
float _Saturation;
float _StepSize;
struct _2v
{
float4 vertex: POSITION;
};
struct v2f
{
float4 pos: SV_POSITION;
};
v2f vert(_2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i): SV_Target
{
//iResolution -> _ScreenParams
//fragCoord -> pos(SV_POSITION)
//iTime:ShaderToy提供的時間函式 -> _Time.y
fixed2 uv = i.pos.xy / _ScreenParams.xy - 0.5;
uv.y *= _ScreenParams.y / _ScreenParams.x;
float3 dir = float3(uv * _Zoom, 1.0);
float time = _Time.y * _Speed + 0.25;
float a1 = 0.5 + 0.5 / _ScreenParams.x * 2.0;
float a2 = 0.8 + 0.5 / _ScreenParams.y * 2.0;
float2x2 Rot1 = float2x2(cos(a1), sin(a1), -sin(a1), cos(a1));
float2x2 Rot2 = float2x2(cos(a2), sin(a2), -sin(a2), cos(a2));
dir.xz = mul(dir.xz, Rot1);
dir.xy = mul(dir.xy, Rot2);
float3 from = float3(1.0, 0.5, 0.5);
from += float3(time * 2.0, time, -2.0);
from.xz = mul(from.xz, Rot1);
from.xy = mul(from.xy, Rot2);
float s = 0.1;
float fade = 1.0;
float3 v = float3(0.0, 0.0, 0.0);
for (int r = 0; r < _Volsteps; r++)
{
float pa = 0.0;
float a = 0.0;
float3 p = from + s * dir * 0.5;
p = abs(float3(_Tile, _Tile, _Tile) - float3(fmod(p.x, _Tile * 2.0), fmod(p.y, _Tile * 2.0), fmod(p.z, _Tile * 2.0))); //tiling fold
for (int i = 0; i < _Iterations; i++)
{
p = abs(p) / dot(p, p) - _Formuparam; //the magic formula
a += abs(length(p) - pa); //absolute sum of average change
pa = length(p);
}
float dm = max(0.0, _Darkmatter - a * a * 0.001); //dark matter
a *= a*a; //add contrast
if (r > 6)
fade *= 1.0 - dm; //dark matter, don't render near
v += fade;
v += float3(s, s * s, s * s * s * s) * a * _Brightness * fade; //coloring based on distance
fade *= _Distfading; //distance fading
s += _StepSize;
}
//mix -> lerp
v = lerp(float3(length(v), length(v), length(v)), v, _Saturation); //color adjust
fixed4 fragColor = float4(v * 0.01, 1.0);
return fragColor;
}
ENDCG
}
}
}
三、支持滑鼠輸入控制
要知道,ShaderToy 對這些大神級的 Shader 做了額外的支持,其中就包括提供每時每刻滑鼠在螢屏中的坐標,也因此,我們可以在效果預覽視窗移動滑鼠來欣賞動態的效果
同理,我們也可以在 C# 腳本中通過 Input.mousePosition 和 Input.GetMouseButton() API 來傳遞當前滑鼠螢屏坐標到著色器,下面是完整的版本:
using System;
using UnityEngine;
[ExecuteInEditMode]
public class ForShaderToy: PostEffectsBase
{
public int horizontal = 1920;
public int vertical = 1080;
public bool isMouseScreenPos = false;
public float mouseSpeed = 0.4f;
public Shader shaderToy;
private Material _material;
private float mouse_x = 0.5f;
private float mouse_y = 0.5f;
public Material material
{
get
{
_material = CheckShaderAndCreateMaterial(shaderToy, _material);
return _material;
}
}
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
RenderTexture scaled = RenderTexture.GetTemporary(horizontal, vertical, 24);
if (Input.GetMouseButton(0))
{
mouse_x = Mathf.Clamp(Input.mousePosition.x / Screen.width, 0, 1);
mouse_y = Mathf.Clamp(Input.mousePosition.y / Screen.height, 0, 1);
if (!isMouseScreenPos)
{
mouse_x = Input.mousePosition.x;
mouse_y = Input.mousePosition.y;
}
mouse_x *= mouseSpeed;
mouse_y *= mouseSpeed;
}
material.SetFloat("_MouseX", mouse_x);
material.SetFloat("_MouseY", mouse_y);
Graphics.Blit(src, scaled, material);
Graphics.Blit(scaled, dest);
RenderTexture.ReleaseTemporary(scaled);
}
else
Graphics.Blit(src, dest);
}
}
//https://www.shadertoy.com/view/XlfGRj
//https://msdn.microsoft.com/en-GB/library/windows/apps/dn166865.aspx
Shader "ShaderToy/StarNest"
{
Properties
{
_Zoom("Zoom", Float) = 0.8 //縮放
_Speed("Speed", Float) = 0.01 //速度
_Volsteps("Volsteps", Int) = 20
_Tile("Tile", Float) = 0.85
_Iterations("Iterations", Int) = 17
_Formuparam("Formuparam", Float) = 0.53
_Brightness("Brightness", Float) = 0.0015
_Darkmatter("Darkmatter", Float) = 0.300
_Distfading("Distfading", Float) = 0.730
_Saturation("Saturation", Float) = 0.850
_StepSize("StepSize", Float) = 0.1
_MouseX("MouseX", Range(0.0, 1.0)) = 0.5
_MouseY("ouseY", Range(0.0, 1.0)) = 0.5
}
SubShader
{
PASS
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Zoom;
float _Speed;
int _Volsteps;
float _Tile;
float _Formuparam;
int _Iterations;
float _Brightness;
float _Darkmatter;
float _Distfading;
float _Saturation;
float _StepSize;
float _MouseX;
float _MouseY;
struct _2v
{
float4 vertex: POSITION;
};
struct v2f
{
float4 pos: SV_POSITION;
};
v2f vert(_2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i): SV_Target
{
//iResolution -> _ScreenParams
//fragCoord -> pos(SV_POSITION)
//iTime:ShaderToy提供的時間函式 -> _Time.y
fixed2 uv = i.pos.xy / _ScreenParams.xy - 0.5;
uv.y *= _ScreenParams.y / _ScreenParams.x;
float3 dir = float3(uv * _Zoom, 1.0);
float time = _Time.y * _Speed + 0.25;
float a1 = 0.5 + _MouseX / _ScreenParams.x * 2.0;
float a2 = 0.8 + _MouseY / _ScreenParams.y * 2.0;
float2x2 Rot1 = float2x2(cos(a1), sin(a1), -sin(a1), cos(a1));
float2x2 Rot2 = float2x2(cos(a2), sin(a2), -sin(a2), cos(a2));
dir.xz = mul(dir.xz, Rot1);
dir.xy = mul(dir.xy, Rot2);
float3 from = float3(1.0, 0.5, 0.5);
from += float3(time * 2.0, time, -2.0);
from.xz = mul(from.xz, Rot1);
from.xy = mul(from.xy, Rot2);
float s = 0.1;
float fade = 1.0;
float3 v = float3(0.0, 0.0, 0.0);
for (int r = 0; r < _Volsteps; r++)
{
float pa = 0.0;
float a = 0.0;
float3 p = from + s * dir * 0.5;
p = abs(float3(_Tile, _Tile, _Tile) - float3(fmod(p.x, _Tile * 2.0), fmod(p.y, _Tile * 2.0), fmod(p.z, _Tile * 2.0))); //tiling fold
for (int i = 0; i < _Iterations; i++)
{
p = abs(p) / dot(p, p) - _Formuparam; //the magic formula
a += abs(length(p) - pa); //absolute sum of average change
pa = length(p);
}
float dm = max(0.0, _Darkmatter - a * a * 0.001); //dark matter
a *= a*a; //add contrast
if (r > 6)
fade *= 1.0 - dm; //dark matter, don't render near
v += fade;
v += float3(s, s * s, s * s * s * s) * a * _Brightness * fade; //coloring based on distance
fade *= _Distfading; //distance fading
s += _StepSize;
}
//mix -> lerp
v = lerp(float3(length(v), length(v), length(v)), v, _Saturation); //color adjust
fixed4 fragColor = float4(v * 0.01, 1.0);
return fragColor;
}
ENDCG
}
}
}
四、GLHL轉HLSL備忘
| GLSL(ShaderToy) | HLSL(Unity3D) | |
|---|---|---|
| iResolution.xy | _ScreenParams.xy | 視口解析度 |
| fragCoord | pos(SV_POSITION) | 當前片元坐標 |
| iTime | _Time.y | 時間函式 |
| vec3 | float3/fixed3/half3 | 向量 |
| mat3 | float3x3 | 矩陣 |
| mix() | lerp() | 平滑插值 |
| mat *= mat | mul(mat, mat) | 矩陣/向量乘法 |
參考資料:
- https://alastaira.wordpress.com/2015/08/07/unity-shadertoys-a-k-a-converting-glsl-shaders-to-cghlsl/
- https://alastaira.wordpress.com/2015/08/07/unity-shadertoys-a-k-a-converting-glsl-shaders-to-cghlsl/
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/266979.html
標籤:其他
上一篇:實習期第一次進專案的體會
下一篇:【游戲開發崗面經總結5】(面相物件和面相程序的區別,多型,CG,設計模式,行程執行緒協程,記憶體區域存放,協程的原理)
