ShaderLanguage
Shader Language目前主要有3种语言:基于OpenGL的OpenGL Shading Language,简称GLSL;基于DirectX的High Level Shading Language,简称HLSL;还有NVIDIA公司的C for Graphic,简称Cg语言。 Unity使用的Shader程序嵌入的小片段是用Cg/HLSL编写的,从“CGPROGRAM”开始,到“CGEND”结束。 ShaderToy使用的WebGL程序由JavaScript编写的句柄和OpenGL Shading Language(GLSL)编写的着色器代码组成,该语言类似于C或C++,并在电脑的图形处理器(GPU)上执行。 所以想在Unity中查看ShaderToy的shader首先要将GLSL语言转化为HLSL/CG语言——
关键词替换
在开始之前,把shadertoy中的代码粘贴到pass下的CGPROGRAM语句段内:
可直接CtrlF替换
Syntax vec
→ float mat → float x fract( → frac( mix( → lerp( texture( → tex2D(/tex3D(/texCUBE KeyWords iTime → _Time.y
需要前文定义
#define atan(x,y) atan2(y,x) #define mod(x,y) x-y*floor(x/y)
手动修改
语义修正
Matrix Multiplication * → mul() 注意顺序:A*=B → A=mul(B,A) A*B → mul(B,A) Type Casting vec3(0) → float3 a = 1或 float3(0,0,0)
需要前文定义/外挂脚本
- iMouse 需要在properties 定义fixed4变量,然后在CG程序块里再次声明同名变量。接下来要外挂C#脚本,可以参考乐乐老师的版本:【ShaderToy】开篇
- Channel 需要在properties 定义对应种类的变量(2D/3D/CUBE),然后在CG程序块里再次声明同名变量。
代码结构
(诚邀美女们下载这个VSCode的粉嫩主题-Dracula Official🍓
以jb猫为例
虽然raymarching modeling is not 4 UNITY,但是它可爱。所以就它了! 除去之前提到的注意事项,还有一点点关于AA的修正:因为没有iResolution,所以smoothstep给了定值;另外在主函数中上下左右取样AA的offset用了ddx(uv.x)来做单位,其实不太确定对不对... 但总之It works! 其实这个写法还是不太规范,uv的运算借用了maintex的运算矩阵来控制offset和scale,但其实没有用到maintex。 注意预览动态材质时要开启alway refreshing: 完整的代码在下面:
Shader "Unlit/Shadertoy"
{
Properties
{
_iChannel0 ("Channel0",CUBE) = "" {}
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
samplerCUBE _iChannel0;
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);;
return o;
}
#define TMIN 0.01
#define TMAX 200.
#define RAYMARCH_TIME 128
#define PRECISION .001
#define AA 3
#define PI 3.1415926
#define S(v,r) smoothstep( r, r+0.005, v )
float logo(float2 uv);
//========SDFunctions========
float sdSphere(float3 p, float3 o, float r){
return length(p-o)-r;
}
float sdCapsule(float3 p,float3 a, float3 b,float r){
float3 ab = b - a;
float3 ap = p - a;
float t = dot(ab, ap)/dot(ab,ab);
t = clamp(t,0.,1.);
float3 c = a +t*ab;
return length(p-c)-r;
}
//https:www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
float sdRoundCone( float3 p, float r1, float r2, float h )
{
// sampling independent computations (only depend on shape)
float b = (r1-r2)/h;
float a = sqrt(1.0-b*b);
// sampling dependant computations
float2 q = float2( length(p.xz), p.y );
float k = dot(q,float2(-b,a));
if( k<0.0 ) return length(q) - r1;
if( k>a*h ) return length(q-float2(0.0,h)) - r2;
return dot(q, float2(a,b) ) - r1;
}
//===============TRANSFORM=================
float2x2 rotate(float a){
return float2x2(cos(a),sin(a),-sin(a),cos(a));
}
float smUni( float d1, float d2, float k ) {
float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
return lerp( d2, d1, h ) - k*h*(1.0-h);
}
//===============RENDER===================
//Scene
float f(float3 p){
p.x=abs(p.x);
float d;
{//face
float3 p0 = float3(p.x*.8,p.y+.4,p.z);
d = sdSphere(p0,float3(0,0,0),1.5);
p0 = float3(p.x*.6,p.y+.6,p.z+.7);
float d0 = sdSphere(p0,float3(0,0,0),1.);
d = smUni(d,d0,.3);
}
float angle = sin(_Time.y*5.)*.1;
{//ear
float3 p0 = float3(p.x-.85,p.y-.3,p.z*1.2+.3);
p0.xy = mul(rotate(-.3),p0.xy);
p0.yz = mul(rotate(-.3),p0.yz);
float d0 = sdRoundCone(p0,.8,.2,1.2)/1.2;
d = smUni(d,d0,.3);
}
{//nose
float d0 = sdCapsule(p,float3(0,0,-1.5),float3(0,-.6,-1.7),.3);
d = smUni(d,d0,.05);
}
{//nose
float d0 = sdSphere(p,float3(.3,-.6,-1.65),.45);
d = smUni(d,d0,.05);
}
{//eye
float3 p0 = float3(p.x-.6,p.y+.1,p.z+1.45);
p0.xy = mul(rotate(-1.2),p0.xy);
p0.yz = mul(rotate(-0.3),p0.yz);
float d0 = sdRoundCone(p0,.06,.08,.2);
d = smUni(d,d0,.05);
}
{//
float3 p0 = float3(p.x-.9,p.y+.5,p.z+1.3);
p0.xy = mul(rotate(.2+angle*.5),p.xy);
float d0 = sdCapsule(p0,float3(0,0,0),float3(.8,0,0),.1);
p0.xy = mul(rotate(-.4+angle*.5),p.xy);
d0 = smUni(d0,sdCapsule(p0,float3(0,0,0),float3(.8,0,0),.1),.05);
d = smUni(d,d0,.05);
}
return d;
}
float rayMarch(in float3 ro, in float3 rd) {
float t = TMIN;
for(int i = 0; i < RAYMARCH_TIME ; i++) {
float3 p = ro + t * rd;
float d = f(p);
t += d;
if(d < PRECISION || t > TMAX)
break;
}
return t;
}
// https://www.iquilezles.org/www/articles/normalsSDF/normalsSDF.htm
float3 calcNormal(in float3 p) {
const float h = 0.0001;
const float2 k = float2(1, -1);
return normalize(k.xyy * f(p + k.xyy * h) +
k.yyx * f(p + k.yyx * h) +
k.yxy * f(p + k.yxy * h) +
k.xxx * f(p + k.xxx * h));
}
float3x3 setCamera(in float3 camtar, in float3 campos, in float camro){
float3 z = normalize(camtar-campos);
float3 cp = float3(sin(camro),cos(camro),0.);
float3 x = normalize(cross(cp,z));
float3 y = cross(z,x);
return float3x3(x,y,z);
}
float3 render(float2 uv){
float3 lightPos = float3(-5., 5.,-5);//light
//SET Camera
float3 cam_tar = float3(0,-.5,0);//cam target
float angle = pow(sin(_Time.y+.3),3.)*.5;
float3 cam_pos = float3(sin(angle),0,-cos(angle))*10.;//cam position
float3 rd = float3(uv,4.); //decide view width
rd = normalize(mul(rd,setCamera(cam_tar,cam_pos,0.)));//viewing frustum
float t = rayMarch(cam_pos,rd);//raymarching
float3 color = lerp(float3(0,0,1),float3(0,0,.6),uv.y*.5+.5);//background
if(t > TMAX) return color;
float3 p = cam_pos + t*rd;
float3 n = calcNormal(p);
float3 l = normalize(lightPos-p);//lightDir
float3 h = normalize(l-rd);//
float3 diffusecol = 1;//diffuse color
float3 specol = 1;//specular color
float3 cmrfl = .5*float3(1,1,1)*texCUBE(_iChannel0,reflect(rd,n)).rgb;//cubemap reflection
float dif = clamp(dot(l,n),0.,1.);//diffuse
float spe = pow(clamp(dot(h,n),0.,1.),50.);//specular
float fresnel = pow(clamp(1. - dot(n,-rd),0.,1.),5.);
color = cmrfl + .1*dif*diffusecol + smoothstep(.6,1.,spe)*specol;//metal
color = smoothstep(0.,1.,color);//add contrast
color += fresnel*.4;
return color;
}
fixed4 frag (v2f i) : SV_Target
{
float2 uv = i.uv - 0.5;//i.uv是0~1,
float3 color = 0;
#if AA>1
for(int m = 0; m < AA; m++) {
for(int n = 0; n < AA; n++) {
float2 offset = 2. * (float2(float(m), float(n)) / float(AA) - .5) * ddx(i.uv.x);
uv = i.uv+offset;
#else
uv = i.uv;
#endif
color += render(uv);
#if AA>1
}
}
color /= float(AA*AA);
#endif
color = lerp(color,float3(1,1,1),logo(uv));
return float4(color,1.0);
}
float logo(float2 uv){
float n = 0.;
n += S(.05,abs(length(uv-float2(-1.23,0))-.12));
n *= 1.-S(-1.25,uv.x);
n += S(.17,abs(uv.y))*S(.05,abs(uv.x+1.29));
n += S(.05,abs(length(uv-float2(-1.5,0))-.12));
n += S(.05,abs(length(uv-float2(-0.9,0))-.12));
n += (1.+S(-.6,uv.x)-S(.05,uv.y))*S(.05,abs(length(uv-float2(-0.6,.03))-.09));
n += (1.-S(-.6,uv.x)+S(-.05,uv.y))*S(.05,abs(length(uv-float2(-0.6,-.04))-.09));
return n;
}
ENDCG
}
}
}