http://lib.csdn.net/article/unity3d/38699
這篇文章翻譯自國外的一篇文章(這里是原文鏈接),正在使用unity的你是否在shader toy上發現很多牛逼哄哄的shader卻不知道如何使用,那么這篇文章就是幫助你來進行轉換的。本文只是基礎文章,那些對HLSL/CG/GLSL都很熟悉的大神相信已經會覺得太簡單了。下面是索引和正文……
本文索引:
如果你正在試圖涉獵shader編程這個領域,你可能或多或少聽說過shaderToy這個網站,這個網站上有很多令人驚奇的shader效果,而這些效果有可能只用了幾行代碼來實現,就好像官方示例程序中提供的效果那樣振奮人心。下面是這個網站上其中兩個很厲害的例子:
“Seascape” by TDM
“Elevated” by iq
這個網站上提供的效果示例不僅僅令人贊嘆,通過查看他們的源代碼,你還可以學習到很多東西。網站上提供了可以在線實時創建和預覽效果的代碼框,在你的瀏覽器中,你可以通過修改代碼,修改變量和輸入來查看效果的變換。
網站上的代碼只提供GLSL語言的支持,因為他們是通過在你的瀏覽器中運行WebGl來展現這些效果的。在本文中的這個例子是在unity5中實現的,但應該在其他版本的unity中也可以很好的運行。
一、 GLSL到HLSL的轉換
理論上來說,應該是有將GLSL直接轉換為HLSL的工具,但據我所知,至少目前我還沒有發現這樣的工具。因此,我只能手動將代碼一行行轉換過來。幸運的是,一般來說,一個shader文件的代碼都不會很長,大部分也就一百來行吧,聽起來可能只需要幾分鍾就可以完成這個過程。
微軟已經發布過一篇文章專門介紹了GLSL和HLSL之間的差別。Unity中也有一篇類似的很有用的文章在這里。而下面是我在轉換shader的過程中經常用到的一些方法:
- 將 iGlobalTime shader 輸入值轉換為(用來驅動shader中動畫的時間,類似C#中的Time.deltaTime(單位為秒)) _Time.y
- 將 iResolution.xy (“視口的像素分辨率”) 轉換為 _ScreenParams.xy
- 將元素為float2, mat2的向量 vec2 類型轉換為float2x2 等.
- 將 vec3(1) 簡寫的構造器改寫為所有分量都為1的 float3(1,1,1)
- 將 Texture2D 改寫為 Tex2D
- 將 atan(x,y) 改寫為 atan2(y,x) <- 注意參數的順序!
- 將 mix() 改寫為 lerp()
- 將 *= 改寫為 mul()
- 將紋理Texture2D查找函數的第三個參數(偏移量bias)移除
- mainImage(out vec4 fragColor, in vec2 fragCoord) 是一個片段着色器, 在unity中等同於 float4 mainImage(float2 fragCoord : SV_POSITION) : SV_Target
- GLSL中紋理坐標Y方向的原點在頂部,而HLSL中紋理坐標Y方向的原點在底部,所以你需要使用這個公式uv.y = 1 – uv.y 對每個點重新定義紋理坐標
ShaderToys上的這些效果都沒有用到頂點着色器函數,他們通過在像素着色器中計算屏幕上每個點對應的UV坐標來確定每個像素對應的顏色值。因此,shadertoy上的這些效果特別適合用來做全屏特效(或者,你可以將他們直接賦給一個面板或者四邊形),只要是UV從0到1平鋪開來的效果都會差不多。
二、 在Unity中實現它
寫過shader的開發者應該清楚,在片段着色器(或者說頂點着色器)中對每個像素單獨做計算是非常耗性能的。如果你想使你的shader以一個流暢運行的幀率來展現的話,一個比較通用的做法是將片段着色器中的像素縮小到一個合適的比率,再通過放大到屏幕比例的方式來減少計算量。以下是本文中要用到的CS代碼通用框架:
using System; using UnityEngine; [ExecuteInEditMode] public class ShaderToy : MonoBehaviour { public int horizontalResolution = 320; public int verticalResolution = 240; public Shader shaderToy; private Material shaderToyMaterial = null; public Material material { get { shaderToyMaterial = CheckShaderAndCreateMaterial(shaderToy, shaderToyMaterial); return shaderToyMaterial; } } // Called by camera to apply image effect void OnRenderImage(RenderTexture source, RenderTexture destination) { // To draw the shader at full resolution, use: // Graphics.Blit (source, destination, material); // To draw the shader at scaled down resolution, use: RenderTexture scaled = RenderTexture.GetTemporary(horizontalResolution, verticalResolution); Graphics.Blit(source, scaled, material); Graphics.Blit(scaled, destination); RenderTexture.ReleaseTemporary(scaled); } protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) { if (shader == null) { return null; } if (shader.isSupported && material && material.shader == shader) return material; if (!shader.isSupported) { return null; } else { material = new Material(shader); material.hideFlags = HideFlags.DontSave; if (material) return material; else return null; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
下面是ShaderToy網站上的源碼:
// Created by inigo quilez - iq/2013 // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = -1.0 + 2.0*fragCoord.xy / iResolution.xy; uv.x *= iResolution.x / iResolution.y; // background vec3 color = vec3(0.8 + 0.2*uv.y); // bubbles for( int i=0; i<40; i++ ) { // bubble seeds float pha = sin(float(i)*546.13+1.0)*0.5 + 0.5; float siz = pow( sin(float(i)*651.74+5.0)*0.5 + 0.5, 4.0 ); float pox = sin(float(i)*321.55+4.1) * iResolution.x / iResolution.y; // buble size, position and color float rad = 0.1 + 0.5*siz; vec2 pos = vec2( pox, -1.0-rad + (2.0+2.0*rad)*mod(pha+0.1*iGlobalTime*(0.2+0.8*siz),1.0)); float dis = length( uv - pos ); vec3 col = mix( vec3(0.94,0.3,0.0), vec3(0.1,0.4,0.8), 0.5+0.5*sin(float(i)*1.2+1.9)); // col+= 8.0*smoothstep( rad*0.95, rad, dis ); // render float f = length(uv-pos)/rad; f = sqrt(clamp(1.0-f*f,0.0,1.0)); color -= col.zyx *(1.0-smoothstep( rad*0.95, rad, dis )) * f; } // vigneting color *= sqrt(1.5-0.5*length(uv)); fragColor = vec4(color,1.0); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
下面是轉換過來在Unity中使用的Shader代碼:
// See https://www.shadertoy.com/view/4dl3zn // GLSL -> HLSL reference: https://msdn.microsoft.com/en-GB/library/windows/apps/dn166865.aspx Shader "Custom/Bubbles" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct v2f{ float4 position : SV_POSITION; }; v2f vert(float4 v:POSITION) : SV_POSITION { v2f o; o.position = mul (UNITY_MATRIX_MVP, v); return o; } fixed4 frag(v2f i) : SV_Target { float2 uv = -1.0 + 2.0*i.position.xy/ _ScreenParams.xy; uv.x *= _ScreenParams.x/ _ScreenParams.y ; // Background fixed4 outColour = fixed4(0.8+0.2*uv.y,0.8+0.2*uv.y,0.8+0.2*uv.y,1); // Bubbles for (int i = 0; i < 40; i++) { // Bubble seeds float pha = sin(float(i)*546.13+1.0)*0.5 + 0.5; float siz = pow( sin(float(i)*651.74+5.0)*0.5 + 0.5, 4.0 ); float pox = sin(float(i)*321.55+4.1); // Bubble size, position and color float rad = 0.1 + 0.5*siz; float2 pos = float2( pox, -1.0-rad + (2.0+2.0*rad)*fmod(pha+0.1*_Time.y*(0.2+0.8*siz),1.0)); float dis = length(uv-pos); float3 col = lerp( float3(0.94,0.3,0.0), float3(0.1,0.4,0.8), 0.5+0.5*sin(float(i)*1.2+1.9)); // Add a black outline around each bubble col+= 8.0*smoothstep( rad*0.95, rad, dis ); // Render float f = length(uv-pos)/rad; f = sqrt(clamp(1.0-f*f,0.0,1.0)); outColour.rgb -= col.zyx *(1.0-smoothstep( rad*0.95, rad, dis )) * f; } // Vignetting outColour *= sqrt(1.5-0.5*length(uv)); return outColour; } ENDCG } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
三、 最終效果
將CS代碼拖到攝像機上,然后參考下圖修改設置就可以看到效果啦,注意腳本組件中的分辨率要和當前屏幕保持一致才不會變形。
參考設置如下: 
最終效果如圖: 
