美文网首页shader
【光能蜗牛的图形学之旅】ShaderToy

【光能蜗牛的图形学之旅】ShaderToy

作者: 光能蜗牛 | 来源:发表于2017-09-07 17:51 被阅读36次

    本篇参考乐乐同学
    而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~

    相关文章

      网友评论

        本文标题:【光能蜗牛的图形学之旅】ShaderToy

        本文链接:https://www.haomeiwen.com/subject/dfdodxtx.html