【光能蜗牛的图形学之旅】ShaderToy
ShaderToy简单介绍
Shadertoy的特点就是大家使用程序来产生各种模型、纹理、动画,所以让人惊叹!一个pixel shader+几张固定的简单的纹理,就能得到这么绚丽的结果!iq的这个新效果更是展示了这种方法能做到的程度——照片级的效果。
这个网上的所有shader都是GLSL的pixel shaders。
那么什么是pixel shader呢?如果我们需要渲染一个刚好铺盖整个屏幕的全屏的方形平板,那么这个方形的fragment shader就是一个pixel shader。这是因为此时,每一个fragment对应了屏幕上的一个pixel。也因此,pixel shader的很多输入都是相同的。在ShaderToy的每个shader上方,你都可以看到一个Shader Input:
uniform vec3 iResolution; // viewport resolution (in pixels) 视口分辨率
uniform float iTime; // shader playback time (in seconds) shader的时间回调
uniform float iTimeDelta; // render time (in seconds) 每一帧的渲染时间
uniform int iFrame; // shader playback frame 帧号
uniform float iChannelTime[4]; // channel playback time (in seconds)
uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
uniform vec4 iMouse; // 鼠标点击位置 mouse pixel coords. xy: current (if MLB down), zw: click
uniform samplerXX iChannel0..3; // input channel. XX = 2D/Cube
uniform vec4 iDate; // (year, month, day, time in seconds) 日期
uniform float iSampleRate; // sound sample rate (i.e., 44100) 采样速率
这些就是ShaderToy提供的公共变量,我们可以直接访问。例如,iResolution存储了屏幕分辨率,iGlobalTime提供了shader运行时间,iMouse提供了鼠标点击位置等等。
由于ShaderToy针对的是pixel shaders,这也意味着它们的vertex shaders都是一样的,只需要计算基本的顶点位置和屏幕位置即可。
如何在unity中使用呢
之前也说了,因为ShaderToy基本就是对pixShader,即片元着色器的编写
我们可以在这个位置看到右边的着色器输入框的代码
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
fragColor = vec4(uv,0.5+0.5*sin(iTime),1.0);
}
它的输入是一个类型为vec2的fragCoord,对应输入的屏幕位置;
它的输出是一个vec4的fragColor,对应该pixel的颜色。很简单对不对!
我们可以简单的在网站中修改这段代码预览一些效果,这就是基本思路
但是在unity中,我们要怎么做呢
闲话不说先上代码
Shader "Shadertoy/Template" {
Properties{
iMouse ("Mouse Pos", Vector) = (100, 100, 0, 0)
iChannel0("iChannel0", 2D) = "white" {}
iChannelResolution0 ("iChannelResolution0", Vector) = (100, 100, 0, 0)
}
CGINCLUDE
#include "UnityCG.cginc"
#pragma target 3.0
#define vec2 float2
#define vec3 float3
#define vec4 float4
#define mat2 float2x2
#define mat3 float3x3
#define mat4 float4x4
#define iGlobalTime _Time.y
#define mod fmod
#define mix lerp
#define fract frac
#define texture2D tex2D
#define iResolution _ScreenParams
#define gl_FragCoord ((_iParam.scrPos.xy/_iParam.scrPos.w) * _ScreenParams.xy)
#define PI2 6.28318530718
#define pi 3.14159265358979
#define halfpi (pi * 0.5)
#define oneoverpi (1.0 / pi)
fixed4 iMouse;
sampler2D iChannel0;
fixed4 iChannelResolution0;
struct v2f {
float4 pos : SV_POSITION;
float4 scrPos : TEXCOORD0;
};
v2f vert(appdata_base v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.scrPos = ComputeScreenPos(o.pos);
return o;
}
vec4 main(vec2 fragCoord);
fixed4 frag(v2f _iParam) : COLOR0 {
vec2 fragCoord = gl_FragCoord;
return main(gl_FragCoord);
}
vec4 main(vec2 fragCoord) {
return vec4(1, 1, 1, 1);
}
ENDCG
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
FallBack Off
}
前面我们说了,shaderToy的代码是基于GLSL所编写,因此转换到Unity ShaderLab就需要一些衔接,而衔接的方式即是在开头定义一系列宏来对应ShaderToy中的GLSL。
其中main函数对应了之前的mainImage函数。
在后面,我们只需要在CGINCLUDE中定义其他函数,并填充main函数即可。
为了可以响应鼠标操作,我们还可以写一个C#脚本,以便在鼠标进行拖拽时将鼠标位置传递给shader。
using UnityEngine;
using System.Collections;
public class ShaderToyHelper : MonoBehaviour {
private Material _material = null;
private bool _isDragging = false;
// Use this for initialization
void Start () {
Renderer render = GetComponent<Renderer>();
if (render != null) {
_material = render.material;
}
_isDragging = false;
}
// Update is called once per frame
void Update () {
Vector3 mousePosition = Vector3.zero;
if (_isDragging) {
mousePosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 1.0f);
} else {
mousePosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0.0f);
}
if (_material != null) {
_material.SetVector("iMouse", mousePosition);
}
}
void OnMouseDown() {
_isDragging = true;
}
void OnMouseUp() {
_isDragging = false;
}
}
代码很简单,在有鼠标拖拽时,mousePositon的Z分量为1,否则为0。这跟ShaderToy中判断鼠标的方式一致。
使用时,只要把该脚本拖拽到材质所在的物体上,同时保证该物体上有绑定Collider即可。
验证鼠标是否有效
你可以尝试将刚才的shader代码改为
vec4 main(vec2 fragCoord) {
// return vec4(1, 1, 1, 1);
return normalize(iMouse);
}
运行程序,可以看到你鼠标移动的时候,物体的颜色发生了改变,
Over~