ShaderToy

ShaderToy

如果你还没听过ShaderToy,那你就真的错过了一个很好的shader学习网站。我是在群里有一次听到小伙伴们提到这个网站的。点进去就会发现里面有很多很绚丽的shader展示。

说简单点,这个网站是专门让人们分享和编写GLSL的pixel shaders的。换句话说,里面那些绚丽的效果仅仅依靠pixel shaders就可以完成了(当然还有纹理和模型的配合,如有不对请指出),是不是很强大?里面的强人很多,就像头脑风暴一样,让你一次次发出惊叹,原来还可以这样做!但是,这里面也蕴含了很多数学和算法知识,所以你可能会经常发现自己脑袋不够用,跟不上作者的思路。。。不过,脑袋都是靠锻炼的嘛,没有捷径可走,多看多写总是没错的~

强烈建议大家先去逛一逛,有很多很好玩的东西,例如这个人写了一个莫比乌斯带,而这个人写了一个耀眼的小太阳!一开始你很难相信这些完全都是用shader计算出来的。

特点

之前说了,这个网上的所有shader都是GLSL的pixel shaders。那么什么是pixel shader呢?如果我们需要渲染一个刚好铺盖整个屏幕的全屏的方形平板,那么这个方形的fragment shader就是一个pixel shader。这是因为此时,每一个fragment对应了屏幕上的一个pixel。也因此,pixel shader的很多输入都是相同的。在ShaderToy的每个shader上方,你都可以看到一个Shader Input:

<code class="language-c++ hljs glsl has-numbering"><span class="hljs-keyword">uniform</span> <span class="hljs-keyword">vec3</span>      iResolution;           <span class="hljs-comment">// viewport resolution (in pixels)</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-keyword">float</span>     iGlobalTime;           <span class="hljs-comment">// shader playback time (in seconds)</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-keyword">float</span>     iChannelTime[<span class="hljs-number">4</span>];       <span class="hljs-comment">// channel playback time (in seconds)</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-keyword">vec3</span>      iChannelResolution[<span class="hljs-number">4</span>]; <span class="hljs-comment">// channel resolution (in pixels)</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-keyword">vec4</span>      iMouse;                <span class="hljs-comment">// mouse pixel coords. xy: current (if MLB down), zw: click</span>
<span class="hljs-keyword">uniform</span> samplerXX iChannel0.<span class="hljs-number">.3</span>;          <span class="hljs-comment">// input channel. XX = 2D/Cube</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-keyword">vec4</span>      iDate;                 <span class="hljs-comment">// (year, month, day, time in seconds)</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-keyword">float</span>     iSampleRate;           <span class="hljs-comment">// sound sample rate (i.e., 44100)</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

这些就是ShaderToy提供的公共变量,我们可以直接访问。例如,iResolution存储了屏幕分辨率,iGlobalTime提供了shader运行时间,iMouse提供了鼠标点击位置等等。

由于ShaderToy针对的是pixel shaders,这也意味着它们的vertex shaders都是一样的,只需要计算基本的顶点位置和屏幕位置即可。

在Unity中实践

Unity的出现的确给很多shader学习者一个方便的编译和展示平台,我们不再需要每次都用很多代码准备好顶点数据,每次都设置纹理,每次都设置MVP矩阵等等。Unity提供给我们更方便的可视化操作。

ShaderToy中很多shader都可以经过一点修改后在Unity中编译运行。当然,也有人这么干过了。例如这位博主

为了方便后面的编写,我们可以写一个针对ShaderToy的基本模板shader。在这之前,我们有必要弄清楚ShaderToy的Shaders长什么样。如果你仔细观察,其实ShaderToy中的所有shader只有一个必不可少的函数:

<code class="language-c++ hljs glsl has-numbering"><span class="hljs-keyword">void</span> mainImage( <span class="hljs-keyword">out</span> <span class="hljs-keyword">vec4</span> fragColor, <span class="hljs-keyword">in</span> <span class="hljs-keyword">vec2</span> fragCoord )</code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

它的输入是一个类型为vec2的fragCoord,对应输入的屏幕位置;输出一个vec4的fragColor,对应该pixel的颜色。很简单对不对!

那么现在我们就可以写出这个模板shader了。

<code class="language-c++ hljs cs has-numbering">Shader <span class="hljs-string">"shadertoy/Template"</span> {
    Properties {
    }

    CGINCLUDE

    <span class="hljs-preprocessor">#include "UnityCG.cginc"  </span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">pragma</span> target 3.0  </span>

    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> vec2 float2</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> vec3 float3</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> vec4 float4</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> mat2 float2x2</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> iGlobalTime _Time.y</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> mod fmod</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> mix lerp</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> atan atan2</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> fract frac </span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> texture2D tex2D</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> iResolution _ScreenParams</span>
    <span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> gl_FragCoord ((_iParam.srcPos.xy/_iParam.srcPos.w)*_ScreenParams.xy)  </span>


    <span class="hljs-keyword">struct</span> vertOut {      
        float4 pos : SV_POSITION;      
        float4 srcPos : TEXCOORD0;    
     };    

     vertOut vert(appdata_base v) {    
         vertOut o;    
         o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
         o.srcPos = ComputeScreenPos(o.pos);   
         <span class="hljs-keyword">return</span> o;    
     }

     vec4 main(vec2 fragCoord);

     fixed4 frag(vertOut _iParam) : COLOR0 {  
        vec2 fragCoord = gl_FragCoord;
        <span class="hljs-keyword">return</span> main(fragCoord);
     }  

     vec4 main(vec2 fragCoord) {
        <span class="hljs-comment">// Your code here</span>
        <span class="hljs-keyword">return</span> vec4(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>);
    }

    ENDCG

    SubShader {
        Pass {
            CGPROGRAM

            <span class="hljs-preprocessor">#<span class="hljs-keyword">pragma</span> vertex vert</span>
            <span class="hljs-preprocessor">#<span class="hljs-keyword">pragma</span> fragment frag</span>
            <span class="hljs-preprocessor">#<span class="hljs-keyword">pragma</span> fragmentoption ARB_precision_hint_fastest   </span>

            ENDCG
        }
    } 
    FallBack <span class="hljs-string">"Diffuse"</span>
}
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li></ul>

代码比较简单。主要是在开头定义了一系列宏来和ShaderToy中的GLSL衔接。其中main函数对应了之前的mainImage函数。在后面,我们只需要在CGINCLUDE中定义其他函数,并填充main函数即可。

写在最后

ShaderToy绝大部分代码都是依靠强大的数学计算来完成的,因此真的是一次次头脑风暴。当然,一些人会觉得它对于现在火爆的移动终端来说用处不大,因为支持不了呗~不过,我还没工作,觉得锻炼下挺好的~在后面我会尽量定期每周更新一篇ShaderToy中的shader。总之,希望大家have fun~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值