您的位置:首页 > 移动开发 > Unity3D

Unity中的ShaderToys——将大神们写的shader搬到unity中来吧

2016-06-07 18:42 525 查看
这篇文章翻译自国外的一篇文章(这里是原文链接),正在使用unity的你是否在shader toy上发现很多牛逼哄哄的shader却不知道如何使用,那么这篇文章就是帮助你来进行转换的。本文只是基础文章,那些对HLSL/CG/GLSL都很熟悉的大神相信已经会觉得太简单了。下面是索引和正文……

本文索引:

一 GLSL到HLSL的转换

二 在Unity中实现它

三 最终效果

如果你正在试图涉猎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;
}
}
}


下面是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);
}


下面是转换过来在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
}
}
}


三、 最终效果

将CS代码拖到摄像机上,然后参考下图修改设置就可以看到效果啦,注意脚本组件中的分辨率要和当前屏幕保持一致才不会变形。

参考设置如下:



最终效果如图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: