Shader初学,心血来潮做了个极光效果,还请各位大佬批评指正。
既然是极光,那自然是少不了夜空的衬托。先给把底色打好:
代码语言:javascript复制 float p = normalize(i.texcoord).y;
float p1 = 1.0f - pow (min (1.0f, 1.0f - p), _Exponent1);
float p3 = 1.0f - pow (min (1.0f, 1.0f p), _Exponent2);
float p2 = 1.0f - p1 - p3;
float4 skyCol = (_Color1 * p1 _Color2 * p2 _Color3 * p3) * _Intensity;
用一个简单的噪声图模拟星空:
代码语言:javascript复制 // 星空散列哈希
float StarAuroraHash(float3 x) {
float3 p = float3(dot(x,float3(214.1 ,127.7,125.4)),
dot(x,float3(260.5,183.3,954.2)),
dot(x,float3(209.5,571.3,961.2)) );
return -0.001 _StarIntensity*frac(sin(p)*43758.5453123);
}
// 星空噪声
float StarNoise(float3 st){
// 卷动星空
st = float3(0,_Time.y*_StarSpeed,0);
// fbm
float3 i = floor(st);
float3 f = frac(st);
float3 u = f*f*(3.0-1.0*f);
return lerp(lerp(dot(StarAuroraHash( i float3(0.0,0.0,0.0)), f - float3(0.0,0.0,0.0) ),
dot(StarAuroraHash( i float3(1.0,0.0,0.0)), f - float3(1.0,0.0,0.0) ), u.x),
lerp(dot(StarAuroraHash( i float3(0.0,1.0,0.0)), f - float3(0.0,1.0,0.0) ),
dot(StarAuroraHash( i float3(1.0,1.0,0.0)), f - float3(1.0,1.0,0.0) ), u.y), u.z) ;
}
可以得到这么一个噪声:
再对每个格子中的颜色进行处理,将阈值范围内的点保留下来:
代码语言:javascript复制 // 星星
float star =StarNoise(fixed3(i.texcoord.x,i.texcoord.y * reflection,i.texcoord.z) * 64);
float4 starOriCol = float4(_StarColor.r 3.25*sin(i.texcoord.x) 2.45 * (sin(_Time.y * _StarSpeed) 1)*0.5,
_StarColor.g 3.85*sin(i.texcoord.y) 1.45 * (sin(_Time.y * _StarSpeed) 1)*0.5,
_StarColor.b 3.45*sin(i.texcoord.z) 4.45 * (sin(_Time.y * _StarSpeed) 1)*0.5,
_StarColor.a 3.85*star);
star = star > 0.8 ? star:smoothstep(0.81,0.98,star);
//...
skyCol = skyCol*(1 - starCol.a) starCol * starCol.a;
满天星斗就出现啦:
再给天空加点简陋的云,这里用了这么一个fbm:
代码语言:javascript复制 // 云散列哈希
float CloudHash (float2 st) {
return frac(sin(dot(st.xy,
float2(12.9898,78.233)))*
43758.5453123);
}
// 云噪声
float CloudNoise (float2 st,int flow) {
// 云卷动
st = float2(0,_Time.y*_CloudSpeed*flow);
float2 i = floor(st);
float2 f = frac(st);
float a = CloudHash(i);
float b = CloudHash(i float2(1.0, 0.0));
float c = CloudHash(i float2(0.0, 1.0));
float d = CloudHash(i float2(1.0, 1.0));
float2 u = f * f * (3.0 - 2.0 * f);
return lerp(a, b, u.x)
(c - a)* u.y * (1.0 - u.x)
(d - b) * u.x * u.y;
}
// 云分型
float Cloudfbm (float2 st,int flow) {
float value = 0.0;
float amplitude = .5;
float frequency = 0.;
for (int i = 0; i < 6; i ) {
value = amplitude * CloudNoise(st,flow);
st *= 2.;
amplitude *= .5;
}
return value;
}
参考自:
https://thebookofshaders.com/13/
在这里我没有把云的噪声从球面展平,因为这样刚好可以得到一个在地平线边缘处有向下趋势的效果:
下面开始加入真正的极光!先从这种类似于扰动效果的极光入手:
这里使用了这样一个fbm:
代码语言:javascript复制 //极光噪声
float AuroraHash(float n ) {
return frac(sin(n)*758.5453);
}
float AuroraNoise(float3 x)
{
float3 p = floor(x);
float3 f = frac(x);
float n = p.x p.y*57.0 p.z*800.0;
float res = lerp(lerp(lerp( AuroraHash(n 0.0), AuroraHash(n 1.0),f.x), lerp( AuroraHash(n 57.0), AuroraHash(n 58.0),f.x),f.y),
lerp(lerp( AuroraHash(n 800.0), AuroraHash(n 801.0),f.x), lerp( AuroraHash(n 857.0), AuroraHash(n 858.0),f.x),f.y),f.z);
return res;
}
//极光分型
float Aurorafbm(float3 p )
{
float f = 0.50000*AuroraNoise( p );
p *= 2.02;
f = 0.25000*AuroraNoise( p );
p *= 2.03;
f = 0.12500*AuroraNoise( p );
p *= 2.01;
f = 0.06250*AuroraNoise( p );
p *= 2.04;
f = 0.03125*AuroraNoise( p );
return f*1.032258;
}
float GetAurora(float3 p)
{
p =Aurorafbm(float3(p.x,p.y,0.0)*0.5)*2.25;
float a = smoothstep(.0, .9, Aurorafbm(p*2.)*2.2-1.1);
return a<0.0 ? 0.0 : a;
}
参考自:
https://www.shadertoy.com/view/3slXDB
得到了这么一个效果:
再加入条带状的极光,这里使用了一个比较复杂的fbm来处理极光的颜色
代码语言:javascript复制 float2x2 RotateMatrix(float a){
float c = cos(a);
float s = sin(a);
return float2x2(c,s,-s,c);
}
float tri(float x){
return clamp(abs(frac(x)-0.5),0.01,0.49);
}
float2 tri2(float2 p){
return float2(tri(p.x) tri(p.y),tri(p.y tri(p.x)));
}
// 极光噪声
float SurAuroraNoise(float2 pos)
{
float intensity=1.8;
float size=2.5;
float rz = 0;
pos = mul(RotateMatrix(pos.x*0.06),pos);
float2 bp = pos;
for (int i=0; i<5; i )
{
float2 dg = tri2(bp*1.85)*.75;
dg = mul(RotateMatrix(_Time.y*_AuroraSpeed),dg);
pos -= dg/size;
bp *= 1.3;
size *= .45;
intensity *= .42;
pos *= 1.21 (rz-1.0)*.02;
rz = tri(pos.x tri(pos.y))*intensity;
pos = mul(-float2x2(0.95534, 0.29552, -0.29552, 0.95534),pos);
}
return clamp(1.0/pow(rz*29., 1.3),0,0.55);
}
参考自:
https://www.shadertoy.com/view/XtGGRt
可以得到这么一个噪声:
用另一个fbm获取极光的位置,再在基础位置上逐层做坐标变换与颜色计算:
代码语言:javascript复制 float SurHash(float2 n){
return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453);
}
float4 SurAurora(float3 pos,float3 ro)
{
float4 col = float4(0,0,0,0);
float4 avgCol = float4(0,0,0,0);
// 逐层
for(int i=0;i<30;i )
{
// 坐标
float of = 0.006*SurHash(pos.xy)*smoothstep(0,15, i);
float pt = ((0.8 pow(i,1.4)*0.002)-ro.y)/(pos.y*2.0 0.8);
pt -= of;
float3 bpos = ro pt*pos;
float2 p = bpos.zx;
// 颜色
float noise = SurAuroraNoise(p);
float4 col2 = float4(0,0,0, noise);
col2.rgb = (sin(1.0-float3(2.15,-.5, 1.2) i*_SurAuroraColFactor*0.1)*0.8 0.5)*noise;
avgCol = lerp(avgCol, col2, 0.5);
col = avgCol*exp2(-i*0.065 - 2.5)*smoothstep(0.,5., i);
}
col *= (clamp(pos.y*15. .4,0.,1.));
return col*1.8;
}
最终得到了这样的效果:
再给天空盒加上一层水面。这里用之前的云噪声做了二次利用,不过与之前不同的是,这里为了体现水面给噪声做了展平处理。
代码语言:javascript复制 // 计算水面反射
if(reflection == -1){
// 水面波纹
float c = dot(float3(0,1,0),i.texcoord.xyz);
float3 pos = i.texcoord.xyz * (1.23 / c);
float re = Cloudfbm(pos.xz * 0.5,1);
skyCol.rgb *= (lerp(0.35,1.12,re) - 0.1) ;
float4 reCol = fixed4(skyCol.rgb re*0.085,re*0.05);
skyCol = skyCol*(1 - reCol.a) reCol * reCol.a;
skyCol =_Color2 * p2 * 0.2;
}
得到了这样的效果:
再顺便做个远景山脉:
代码语言:javascript复制 // 远处山脉
float mountain = Cloudfbm(fixed2(lerp(0,1,(i.texcoord.x 1)/2),0.412436) * 10,0);
mountain = (smoothstep(0,(Cloudfbm(fixed2(lerp(0,1,(i.texcoord.x 1)/2),0.412436) * 10,0)),mountain)) * mountain *1.8 - _MountainHeight;
float4 mountainCol = fixed4(_MountainColor.rgb,(abs(i.texcoord.y) < mountain * 0.15) ? 1 : 0);
大功告成!
完整代码如下:
代码语言:javascript复制Shader "Aurora/AuroraSky"
{
Properties
{
[Header(Sky Setting)]
_Color1 ("Top Color", Color) = (1, 1, 1, 0)
_Color2 ("Horizon Color", Color) = (1, 1, 1, 0)
_Color3 ("Bottom Color", Color) = (1, 1, 1, 0)
_Exponent1 ("Exponent Factor for Top Half", Float) = 1.0
_Exponent2 ("Exponent Factor for Bottom Half", Float) = 1.0
_Intensity ("Intensity Amplifier", Float) = 1.0
[Header(Star Setting)]
[HDR]_StarColor ("Star Color", Color) = (1,1,1,0)
_StarIntensity("Star Intensity", Range(0,1)) = 0.5
_StarSpeed("Star Speed", Range(0,1)) = 0.5
[Header(Cloud Setting)]
[HDR]_CloudColor ("Cloud Color", Color) = (1,1,1,0)
_CloudIntensity("Cloud Intensity", Range(0,1)) = 0.5
_CloudSpeed("CloudSpeed", Range(0,1)) = 0.5
[Header(Aurora Setting)]
[HDR]_AuroraColor ("Aurora Color", Color) = (1,1,1,0)
_AuroraIntensity("Aurora Intensity", Range(0,1)) = 0.5
_AuroraSpeed("AuroraSpeed", Range(0,1)) = 0.5
_SurAuroraColFactor("Sur Aurora Color Factor", Range(0,1)) = 0.5
[Header(Envirment Setting)]
[HDR]_MountainColor ("Mountain Color", Color) = (1,1,1,0)
_MountainFactor("Mountain Factor", Range(0,1)) = 0.5
_MountainHeight("Mountain Height", Range(0,2)) = 0.5
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
float4 position : POSITION;
float3 texcoord : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
float3 texcoord : TEXCOORD0;
float3 normal : TEXCOORD1;
};
// 环境背景颜色
half4 _Color1;
half4 _Color2;
half4 _Color3;
half _Intensity;
half _Exponent1;
half _Exponent2;
//星星
half4 _StarColor;
half _StarIntensity;
half _StarSpeed;
// 云
half4 _CloudColor;
half _CloudIntensity;
half _CloudSpeed;
// 极光
half4 _AuroraColor;
half _AuroraIntensity;
half _AuroraSpeed;
half _SurAuroraColFactor;
// 远景山
half4 _MountainColor;
float _MountainFactor;
half _MountainHeight;
v2f vert (appdata v)
{
v2f o;
o.position = UnityObjectToClipPos (v.position);
o.texcoord = v.texcoord;
o.normal = v.normal;
return o;
}
// 星空散列哈希
float StarAuroraHash(float3 x) {
float3 p = float3(dot(x,float3(214.1 ,127.7,125.4)),
dot(x,float3(260.5,183.3,954.2)),
dot(x,float3(209.5,571.3,961.2)) );
return -0.001 _StarIntensity*frac(sin(p)*43758.5453123);
}
// 星空噪声
float StarNoise(float3 st){
// 卷动星空
st = float3(0,_Time.y*_StarSpeed,0);
// fbm
float3 i = floor(st);
float3 f = frac(st);
float3 u = f*f*(3.0-1.0*f);
return lerp(lerp(dot(StarAuroraHash( i float3(0.0,0.0,0.0)), f - float3(0.0,0.0,0.0) ),
dot(StarAuroraHash( i float3(1.0,0.0,0.0)), f - float3(1.0,0.0,0.0) ), u.x),
lerp(dot(StarAuroraHash( i float3(0.0,1.0,0.0)), f - float3(0.0,1.0,0.0) ),
dot(StarAuroraHash( i float3(1.0,1.0,0.0)), f - float3(1.0,1.0,0.0) ), u.y), u.z) ;
}
// 云散列哈希
float CloudHash (float2 st) {
return frac(sin(dot(st.xy,
float2(12.9898,78.233)))*
43758.5453123);
}
// 云噪声
float CloudNoise (float2 st,int flow) {
// 云卷动
st = float2(0,_Time.y*_CloudSpeed*flow);
float2 i = floor(st);
float2 f = frac(st);
float a = CloudHash(i);
float b = CloudHash(i float2(1.0, 0.0));
float c = CloudHash(i float2(0.0, 1.0));
float d = CloudHash(i float2(1.0, 1.0));
float2 u = f * f * (3.0 - 2.0 * f);
return lerp(a, b, u.x)
(c - a)* u.y * (1.0 - u.x)
(d - b) * u.x * u.y;
}
// 云分型
float Cloudfbm (float2 st,int flow) {
float value = 0.0;
float amplitude = .5;
float frequency = 0.;
for (int i = 0; i < 6; i ) {
value = amplitude * CloudNoise(st,flow);
st *= 2.;
amplitude *= .5;
}
return value;
}
//极光噪声
float AuroraHash(float n ) {
return frac(sin(n)*758.5453);
}
float AuroraNoise(float3 x)
{
float3 p = floor(x);
float3 f = frac(x);
float n = p.x p.y*57.0 p.z*800.0;
float res = lerp(lerp(lerp( AuroraHash(n 0.0), AuroraHash(n 1.0),f.x), lerp( AuroraHash(n 57.0), AuroraHash(n 58.0),f.x),f.y),
lerp(lerp( AuroraHash(n 800.0), AuroraHash(n 801.0),f.x), lerp( AuroraHash(n 857.0), AuroraHash(n 858.0),f.x),f.y),f.z);
return res;
}
//极光分型
float Aurorafbm(float3 p )
{
float f = 0.50000*AuroraNoise( p );
p *= 2.02;
f = 0.25000*AuroraNoise( p );
p *= 2.03;
f = 0.12500*AuroraNoise( p );
p *= 2.01;
f = 0.06250*AuroraNoise( p );
p *= 2.04;
f = 0.03125*AuroraNoise( p );
return f*1.032258;
}
float GetAurora(float3 p)
{
p =Aurorafbm(float3(p.x,p.y,0.0)*0.5)*2.25;
float a = smoothstep(.0, .9, Aurorafbm(p*2.)*2.2-1.1);
return a<0.0 ? 0.0 : a;
}
/**************带状极光***************/
// 旋转矩阵
float2x2 RotateMatrix(float a){
float c = cos(a);
float s = sin(a);
return float2x2(c,s,-s,c);
}
float tri(float x){
return clamp(abs(frac(x)-0.5),0.01,0.49);
}
float2 tri2(float2 p){
return float2(tri(p.x) tri(p.y),tri(p.y tri(p.x)));
}
// 极光噪声
float SurAuroraNoise(float2 pos)
{
float intensity=1.8;
float size=2.5;
float rz = 0;
pos = mul(RotateMatrix(pos.x*0.06),pos);
float2 bp = pos;
for (int i=0; i<5; i )
{
float2 dg = tri2(bp*1.85)*.75;
dg = mul(RotateMatrix(_Time.y*_AuroraSpeed),dg);
pos -= dg/size;
bp *= 1.3;
size *= .45;
intensity *= .42;
pos *= 1.21 (rz-1.0)*.02;
rz = tri(pos.x tri(pos.y))*intensity;
pos = mul(-float2x2(0.95534, 0.29552, -0.29552, 0.95534),pos);
}
return clamp(1.0/pow(rz*29., 1.3),0,0.55);
}
float SurHash(float2 n){
return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453);
}
float4 SurAurora(float3 pos,float3 ro)
{
float4 col = float4(0,0,0,0);
float4 avgCol = float4(0,0,0,0);
// 逐层
for(int i=0;i<30;i )
{
// 坐标
float of = 0.006*SurHash(pos.xy)*smoothstep(0,15, i);
float pt = ((0.8 pow(i,1.4)*0.002)-ro.y)/(pos.y*2.0 0.8);
pt -= of;
float3 bpos = ro pt*pos;
float2 p = bpos.zx;
// 颜色
float noise = SurAuroraNoise(p);
float4 col2 = float4(0,0,0, noise);
col2.rgb = (sin(1.0-float3(2.15,-.5, 1.2) i*_SurAuroraColFactor*0.1)*0.8 0.5)*noise;
avgCol = lerp(avgCol, col2, 0.5);
col = avgCol*exp2(-i*0.065 - 2.5)*smoothstep(0.,5., i);
}
col *= (clamp(pos.y*15. .4,0.,1.));
return col*1.8;
}
half4 frag (v2f i) : COLOR
{
// return fixed4(SurAuroraNoise(i.texcoord.xy),SurAuroraNoise(i.texcoord.xy),SurAuroraNoise(i.texcoord.xy),1);
// 底色
float p = normalize(i.texcoord).y;
float p1 = 1.0f - pow (min (1.0f, 1.0f - p), _Exponent1);
float p3 = 1.0f - pow (min (1.0f, 1.0f p), _Exponent2);
float p2 = 1.0f - p1 - p3;
int reflection = i.texcoord.y < 0 ? -1 : 1;
// 云
float cloud = Cloudfbm(fixed2(i.texcoord.x,i.texcoord.z) * 8,1);
float4 cloudCol = float4(cloud * _CloudColor.rgb,cloud*0.8) * _CloudIntensity;
// 星星
float star =StarNoise(fixed3(i.texcoord.x,i.texcoord.y * reflection,i.texcoord.z) * 64);
float4 starOriCol = float4(_StarColor.r 3.25*sin(i.texcoord.x) 2.45 * (sin(_Time.y * _StarSpeed) 1)*0.5,
_StarColor.g 3.85*sin(i.texcoord.y) 1.45 * (sin(_Time.y * _StarSpeed) 1)*0.5,
_StarColor.b 3.45*sin(i.texcoord.z) 4.45 * (sin(_Time.y * _StarSpeed) 1)*0.5,
_StarColor.a 3.85*star);
star = star > 0.8 ? star:smoothstep(0.81,0.98,star);
float4 starCol = fixed4((starOriCol * star).rgb,star);
//带状极光
float4 surAuroraCol = smoothstep(0.0,1.5,SurAurora(
float3(i.texcoord.x,abs(i.texcoord.y),i.texcoord.z),
float3(0,0,-6.7)
)) (reflection-1)*-0.2*0.5;
//极光
float3 aurora = float3 (_AuroraColor.rgb * GetAurora(float3(i.texcoord.xy*float2(1.2,1.0), _Time.y*_AuroraSpeed*0.22)) * 0.9
_AuroraColor.rgb * GetAurora(float3(i.texcoord.xy*float2(1.0,0.7) , _Time.y*_AuroraSpeed*0.27)) * 0.9
_AuroraColor.rgb * GetAurora(float3(i.texcoord.xy*float2(0.8,0.6) , _Time.y*_AuroraSpeed*0.29)) * 0.5
_AuroraColor.rgb * GetAurora(float3(i.texcoord.xy*float2(0.9,0.5) , _Time.y*_AuroraSpeed*0.20)) * 0.57);
float4 auroraCol = float4(aurora,0.1354);
// 远处山脉
float mountain = Cloudfbm(fixed2(lerp(0,1,(i.texcoord.x 1)/2),0.412436) * 10,0);
mountain = (smoothstep(0,(Cloudfbm(fixed2(lerp(0,1,(i.texcoord.x 1)/2),0.412436) * 10,0)),mountain)) * mountain *1.8 - _MountainHeight;
float4 mountainCol = fixed4(_MountainColor.rgb,(abs(i.texcoord.y) < mountain * 0.15) ? 1 : 0);
//混合
float4 skyCol = (_Color1 * p1 _Color2 * p2 _Color3 * p3) * _Intensity;
starCol = reflection==1?starCol:starCol*0.5;
skyCol = skyCol*(1 - starCol.a) starCol * starCol.a;
skyCol = skyCol*(1 - cloudCol.a) cloudCol * cloudCol.a;
skyCol = skyCol*(1 - surAuroraCol.a) surAuroraCol * surAuroraCol.a;
skyCol = skyCol*(1 - auroraCol.a) auroraCol * auroraCol.a;
skyCol = skyCol*(1 - mountainCol.a) mountainCol * mountainCol.a;
// 计算水面反射
if(reflection == -1){
// 水面波纹
float c = dot(float3(0,1,0),i.texcoord.xyz);
float3 pos = i.texcoord.xyz * (1.23 / c);
float re = Cloudfbm(pos.xz * 0.5,1);
skyCol.rgb *= (lerp(0.35,1.12,re) - 0.1) ;
float4 reCol = fixed4(skyCol.rgb re*0.085,re*0.05);
skyCol = skyCol*(1 - reCol.a) reCol * reCol.a;
skyCol =_Color2 * p2 * 0.2;
}
return skyCol;
}
ENDCG
SubShader
{
Tags { "RenderType"="Background" "Queue"="Background" }
Pass
{
ZWrite Off
Cull Off
Fog { Mode Off }
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}