Godot implements screen smoke Shader

foreword

Many games have some high-quality screen effects, and some 3D engines render to the UI through the scene, or even a scene directly. But there are other ways to achieve it, such as pure Shader.

I borrowed from https://www.shadertoy.com/view/XtfSW4

The method is similar to the previous article so I won’t go into details, and interested students can take a look.

code sharing

shader_type canvas_item;

uniform vec3      iResolution;           // 视口分辨率(像素)
uniform float     iTime;                 // 着色器回放时间(单位:秒)

const float cloudDensity = 1.0; 	// overall density [0,1]
const float noisiness = 0.35; 	// overall strength of the noise effect [0,1]
const float speed = 0.1;			// controls the animation speed [0, 0.1 ish)
const float cloudHeight = 2.5; 	// (inverse) height of the input gradient [0,...)


// Simplex noise below = ctrl+c, ctrl+v:
// Description : Array and textureless GLSL 2D/3D/4D simplex 
//               noise functions.
//      Author : Ian McEwan, Ashima Arts.
//  Maintainer : ijm
//     Lastmod : 20110822 (ijm)
//     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
//               Distributed under the MIT License. See LICENSE file.
//               https://github.com/ashima/webgl-noise
// 

vec3 mod289(vec3 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 mod289_(vec4 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 permute(vec4 x) {
     return mod289_(((x*34.0)+1.0)*x);
}

vec4 taylorInvSqrt(vec4 r)
{
  return 1.79284291400159 - 0.85373472095314 * r;
}

float snoise(vec3 v)
  { 
  const vec2  C = vec2(1.0/6.0, 1.0/3.0) ;
  const vec4  D = vec4(0.0, 0.5, 1.0, 2.0);

// First corner
  vec3 i  = floor(v + dot(v, C.yyy) );
  vec3 x0 =   v - i + dot(i, C.xxx) ;

// Other corners
  vec3 g = step(x0.yzx, x0.xyz);
  vec3 l = 1.0 - g;
  vec3 i1 = min( g.xyz, l.zxy );
  vec3 i2 = max( g.xyz, l.zxy );

  //   x0 = x0 - 0.0 + 0.0 * C.xxx;
  //   x1 = x0 - i1  + 1.0 * C.xxx;
  //   x2 = x0 - i2  + 2.0 * C.xxx;
  //   x3 = x0 - 1.0 + 3.0 * C.xxx;
  vec3 x1 = x0 - i1 + C.xxx;
  vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
  vec3 x3 = x0 - D.yyy;      // -1.0+3.0*C.x = -0.5 = -D.y

// Permutations
  i = mod289(i); 
  vec4 p = permute( permute( permute( 
             i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
           + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 
           + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));

// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
  float n_ = 0.142857142857; // 1.0/7.0
  vec3  ns = n_ * D.wyz - D.xzx;

  vec4 j = p - 49.0 * floor(p * ns.z * ns.z);  //  mod(p,7*7)

  vec4 x_ = floor(j * ns.z);
  vec4 y_ = floor(j - 7.0 * x_ );    // mod(j,N)

  vec4 x = x_ *ns.x + ns.yyyy;
  vec4 y = y_ *ns.x + ns.yyyy;
  vec4 h = 1.0 - abs(x) - abs(y);

  vec4 b0 = vec4( x.xy, y.xy );
  vec4 b1 = vec4( x.zw, y.zw );

  //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
  //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
  vec4 s0 = floor(b0)*2.0 + 1.0;
  vec4 s1 = floor(b1)*2.0 + 1.0;
  vec4 sh = -step(h, vec4(0.0));

  vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
  vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;

  vec3 p0 = vec3(a0.xy,h.x);
  vec3 p1 = vec3(a0.zw,h.y);
  vec3 p2 = vec3(a1.xy,h.z);
  vec3 p3 = vec3(a1.zw,h.w);

//Normalise gradients
  vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
  p0 *= norm.x;
  p1 *= norm.y;
  p2 *= norm.z;
  p3 *= norm.w;

// Mix final noise value
  vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
  m = m * m;
  return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 
                                dot(p2,x2), dot(p3,x3) ) );
}

/// Cloud stuff:
const float maximum = 1.0/1.0 + 1.0/2.0 + 1.0/3.0 + 1.0/4.0 + 1.0/5.0 + 1.0/6.0 + 1.0/7.0 + 1.0/8.0;
// Fractal Brownian motion, or something that passes for it anyway: range [-1, 1]
float fBm(vec3 uv)
{
    float sum = 0.0;
    for (int i = 0; i < 8; ++i) {
        float f = float(i+1);
        sum += snoise(uv*f) / f;
    }
    return sum / maximum;
}

// Simple vertical gradient:
float gradient(vec2 uv) {
 	return (1.0 - uv.y * uv.y * cloudHeight);   
}

vec4 mainImage(in vec4 fragCoord )
{
	vec2 uv = fragCoord.xy / iResolution.xy;
    vec3 p = vec3(uv, iTime*speed);
    vec3 someRandomOffset = vec3(0.1, 0.3, 0.2);
    vec2 duv = vec2(fBm(p), fBm(p + someRandomOffset)) * noisiness;
    float q = gradient(uv + duv) * cloudDensity;
	q = clamp(q,0.0, 1.0);
	return vec4(q,q,q, q);
}

void fragment(){
	vec4 output = mainImage(FRAGCOORD);
    COLOR = output;
}

Summarize

I changed a few places, there is no mainImage function in the godot engine, so I need to put the modified function into the fragment function.

And the definition of the mainImage function has been modified, such as void becomes vec4.

Added two variables for program modification

uniform vec3      iResolution;           // 视口分辨率(像素)
uniform float     iTime;                 // 着色器回放时间(单位:秒)

Write const on some variables

const float cloudDensity = 1.0; 	// overall density [0,1]
const float noisiness = 0.35; 	// overall strength of the noise effect [0,1]
const float speed = 0.1;			// controls the animation speed [0, 0.1 ish)
const float cloudHeight = 2.5; 	// (inverse) height of the input gradient [0,...)

And modify the function mod289_ with the same name

vec4 mod289_(vec4 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 permute(vec4 x) {
     return mod289_(((x*34.0)+1.0)*x);
}

Finally release the effect

 

Guess you like

Origin blog.csdn.net/u012863565/article/details/128729612