Im newbie at webgl world and trying to learn something. while after i figured out how shaders work, wanted to go a step further and make a smooth, transparent cloud. Actually i made it, but as you see in the images below, there is some overlaping edges look jagged.
can you explain why this is happening and how to solve it?
thank you
v77
Snippet
var camera, controls, scene, renderer;
var sceneCss, rendererCss;
var dirLight, ambientLight, pointLight;
var displacementMaterial;
var wH = window.innerHeight;
var wW = window.innerWidth;
var start = Date.now();
var properties;
var cloudRadius = 150;
var cloudMesh;
var noiseMaterial, noiseMap;
var uniforms, displamentUniforms;
$(document).ready(function() {
//init Core Engine;
init();
//init All Engines;
onLoaded();
animateAll();
});
//Give it life;
function init() {
properties = {
smoke: 2.0,
heat: 0.0007,
shapeBiasX: 1.5,
shapeBiasY: 2.5,
displacementScale: 40,
displacementBias: -22,
turbulence: 40,
twist: 0,
wireframes: false,
rotationX: .5,
rotationY: 0,
rotationZ: 0,
opacity: 1.0
}
// add camera and controls
camera = new THREE.PerspectiveCamera(70, wW / wH, 0.1, 20000);
camera.position.set(0, 0, 400);
//PostProcess Materials
sceneRenderTarget = new THREE.Scene();
cameraOrtho = new THREE.OrthographicCamera(wW / -2, wW / 2, wH / 2, wH / -2, -10000, 10000);
cameraOrtho.position.z = 100;
cameraOrtho.updateProjectionMatrix();
var plane = new THREE.PlaneGeometry(wW, wH);
quadTarget = new THREE.Mesh(plane, new THREE.MeshBasicMaterial({ transparent: true, opacity: .1, color: 0x000000 }));
quadTarget.position.z = -500;
sceneRenderTarget.add(quadTarget);
//
//scene
scene = new THREE.Scene();
sceneCss = new THREE.Scene();
fog = new THREE.FogExp2(0x212121, 0.002);
scene.fog = fog;
//renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(wW, wH);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.sortObjects = false;
renderer.domElement.style.zIndex = 0;
renderer.setClearColor(0x222222);
document.body.appendChild(renderer.domElement);
}
function onLoaded() {
//Will Check Processes
createClouds();
}
function createClouds() {
uniforms = {
time: {
type: "f",
value: 1.0
},
uSpeed: {
type: "f",
value: 1.0
},
scale: {
type: "v2",
value: new THREE.Vector2(1, 1)
},
opacity: {
type: "f",
value: 1.0
}
};
noiseMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('noise_vertex').textContent,
fragmentShader: document.getElementById('noise_fragment').textContent,
lights: false,
wireframe: properties.wireframes
});
noiseMap = new THREE.WebGLRenderTarget(512, 512, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
wrapS: THREE.RepeatWrapping
});
displacementUniforms = {
time: {
type: "f",
value: 1.0
},
tHeightMap: {
type: "t",
value: noiseMap.texture
},
uDisplacementBias: {
type: "f",
value: properties.displacementBias
},
uDisplacementScale: {
type: "f",
value: properties.displacementScale
},
uColor1: {
type: "c",
value: new THREE.Color(0xffff00)
},
uColor2: {
type: "c",
value: new THREE.Color(0x0000ff)
},
uSmoke: {
type: "f",
value: properties.smoke
},
uShapeBias: {
type: "v2",
value: new THREE.Vector2(properties.shapeBiasX, properties.shapeBiasY)
},
uScreenHeight: {
type: "f",
value: wH
},
uTurbulence: {
type: "f",
value: properties.turbulence
},
uTwist: {
type: "f",
value: properties.twist
},
opacity: {
type: "f",
value: 1.0
}
};
displacementMaterial = new THREE.ShaderMaterial({
wireframe: properties.wireframes,
transparent: true,
uniforms: displacementUniforms,
vertexShader: document.getElementById('displacement_vertex').textContent,
fragmentShader: document.getElementById('displacement_fragment').textContent,
premultipliedAlpha: true,
side: THREE.DoubleSide,
shading: THREE.SmoothShading,
depthTest: false
});
var geometrySphere = new THREE.SphereGeometry(cloudRadius, 140, 100);
geometrySphere.computeFaceNormals();
cloudMesh = new THREE.Mesh(geometrySphere, displacementMaterial);
cloudMesh.position.y = -40;
cloudMesh.renderOrder = 5;
scene.add(cloudMesh);
}
function animateAll() {
uniforms.uSpeed.value += properties.heat;
uniforms.time.value += properties.heat * .3;
displacementUniforms.opacity.value = properties.opacity;
displacementMaterial.uniforms["time"].value += properties.heat * .3;
//cloudMesh.rotation.x = properties.rotationX;
cloudMesh.rotation.z = properties.rotationZ;
//cloudMesh.rotation.y = properties.rotationY;
cloudMesh.rotation.y += 0.002;
requestAnimationFrame(animateAll);
renderAll();
}
//render
function renderAll() {
renderer.clear();
quadTarget.material = noiseMaterial;
renderer.render(sceneRenderTarget, cameraOrtho, noiseMap, true);
renderer.render(scene, camera);
}
canvas {
width: 100%;
height: 100%;
position: absolute;
z-index: -10
}
body {
overflow: hidden;
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.min.js"></script>
<script src="https://code.jquery.com/jquery-1.12.2.js"></script>
<script data-src="shaders/displacement_vertex.js" data-name="Displacement" type="x-shader/x-vertex" id="displacement_vertex">
uniform float time;
uniform vec2 scale;
uniform float uTwist;
varying vec2 vUv;
varying vec3 vNormal;
uniform vec2 uShapeBias;
uniform float uTurbulence;
#ifdef VERTEX_TEXTURES
uniform sampler2D tHeightMap;
uniform float uDisplacementScale;
uniform float uDisplacementBias;
#endif
vec4 DoTwist( vec4 pos, float t )
{
float st = sin(t);
float ct = cos(t);
vec4 new_pos;
new_pos.x = pos.x*ct - pos.z*st;
new_pos.z = pos.x*st + pos.z*ct;
new_pos.y = pos.y;
new_pos.w = pos.w;
return( new_pos );
}
void main( void ) {
vUv = uv;
vNormal = normalize( normalMatrix * normal );
//change matrix
vec4 mPosition = modelMatrix * vec4( position, 1.0 );
mPosition.x *= uShapeBias.x +1.0; // uShapeBias.x*(vUv.x+1.0);
mPosition.y *= (1.0 -(vUv.y-0.5)*-uShapeBias.y);
//mPosition.y -= 40.0;
float turbFactor = uTurbulence*(vUv.y-0.5);
//shape turbulance
mPosition.x += sin(mPosition.y/100.0 + time*20.0 )*turbFactor;
mPosition.z += cos(mPosition.y/100.0 + time*20.0 )*turbFactor;
//twist
float angle_rad = uTwist * 3.14159 / 180.0;
float height = -300.0;
float ang = (position.y-height*0.5)/height * angle_rad;
vec4 twistedPosition = DoTwist(mPosition, ang);
vec4 twistedNormal = DoTwist(vec4(vNormal,1.0), ang);
//change matrix
vec4 mvPosition = viewMatrix * twistedPosition;
#ifdef VERTEX_TEXTURES
vec3 dv = texture2D( tHeightMap, vUv ).xyz;
float df = uDisplacementScale * dv.x + uDisplacementBias;
vec4 displacedPosition = vec4( twistedNormal.xyz * df, 0.0 ) + mvPosition;
gl_Position = projectionMatrix * displacedPosition;
#else
gl_Position = projectionMatrix * mvPosition;
#endif
}
</script>
<script data-src="shaders/displacement_fragment.js" data-name="Displacement" type="x-shader/x-fragment" id="displacement_fragment">
varying vec2 vUv;
uniform sampler2D tHeightMap;
uniform float uSmoke;
uniform vec3 uColor1;
uniform vec3 uColor2;
uniform float uScreenHeight;
void main( void ) {
vec4 heightColor = texture2D( tHeightMap, vUv);
vec3 heightAlpha = texture2D( tHeightMap, vUv).xyz;
vec3 gradient1 = uColor1/(gl_FragCoord.y/uScreenHeight*4.0);
vec3 gradient2 = uColor2/(gl_FragCoord.y/uScreenHeight*4.0);
vec3 fireSumColor = (gradient1+gradient2)*heightColor.b;
float opacity = heightAlpha.x *.05;
//smoke
gl_FragColor = vec4(mix( fireSumColor, vec3(1.0), gl_FragCoord.y/uScreenHeight*uSmoke ), opacity);
float depth = gl_FragCoord.z / gl_FragCoord.w;
float fogFactor = smoothstep( 10.0, 400.0, depth* .9 );
gl_FragColor = mix( gl_FragColor, vec4( vec3(0.0,0.0,0.0), gl_FragColor.w ), fogFactor )*1.0;
// gl_FragColor = gl_FragColor*vec4(vec3(1.0), 1.0);
}
</script>
<script data-src="shaders/noise_vertex.js" data-name="Noise" type="x-shader/x-vertex" id="noise_vertex">
varying vec2 vUv;
uniform vec2 scale;
void main( void ) {
vUv = uv * scale;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script data-src="shaders/noise_fragment.js" data-name="Noise" type="x-shader/x-fragment" id="noise_fragment">
//
// Description : Array and textureless GLSL 3D simplex noise function.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : ijm
// Lastmod : 20110409 (stegu)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
//
uniform float time;
uniform float uSpeed;
varying vec2 vUv;
vec4 permute( vec4 x ) {
return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 );
}
vec4 taylorInvSqrt( vec4 r ) {
return 1.79284291400159 - 0.85373472095314 * r;
}
float PI = 3.14159265;
float TWOPI = 6.28318531;
float BaseRadius = 1.0;
vec3 sphere( float u, float v) {
u *= PI;
v *= TWOPI;
vec3 pSphere;
pSphere.x = BaseRadius * cos(v) * sin(u);
pSphere.y = BaseRadius * sin(v) * sin(u);
pSphere.z = BaseRadius * cos(u);
return pSphere;
}
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 );
vec3 x1 = x0 - i1 + 1.0 * C.xxx;
vec3 x2 = x0 - i2 + 2.0 * C.xxx;
vec3 x3 = x0 - 1. + 3.0 * C.xxx;
// Permutations
i = mod( i, 289.0 );
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
// ( N*N points uniformly over a square, mapped onto an octahedron.)
float n_ = 1.0 / 7.0; // N=7
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor( p * ns.z *ns.z ); // mod(p,N*N)
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 = 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 ) ) );
}
float surface( vec3 coord ) {
float n = 0.0;
n += 0.7 * abs( snoise( coord ) );
n += 0.25 * abs( snoise( coord * 2.0 ) );
n += 0.125 * abs( snoise( coord * 4.0 ) );
n += 0.0625 * abs( snoise( coord * 8.0 ) );
return n;
}
void main( void ) {
vec3 coord = sphere(vUv.y,vUv.x);
coord.x += uSpeed;
coord.y += -time;
coord.z += -time;
float n = surface( coord );
gl_FragColor = vec4( vec3( n, n, n ), 1.0 );
}
</script>
When you have overlapping transparent materials in three.js, you can often remove unwanted artifacts by setting
material.depthTest = false;
It depends on your use case.
It will not work well if you have opaque objects the occlude the transparent objects, however. But that is not your use case here.
EDIT: As #Bahadir points out in the comment below, you can an also try
material.depthWrite = false;
three.js r.77
Related
I'd like to change the background color of a simple three.js scene, like the one you change with
renderer.setClearColor(/*insert color here*/)
or
scene.background = new THREE.Color(/*insert color here*/)
with fragment shaders,
but including glsl code or ShaderMaterial inside the brackets doesnt work.
How can I do it?
Here is an approach with WebGLRenderTarget.texture as scene.background:
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 0, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
gu.resolution.set(innerWidth, innerHeight);
});
let gu = {
time: {value: 0},
resolution: {value: new THREE.Vector2(innerWidth, innerHeight)}
}
let light = new THREE.DirectionalLight(0xffffff, 0.8);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.2));
let ico = new THREE.Mesh(new THREE.IcosahedronGeometry(3, 0), new THREE.MeshLambertMaterial({color: 0xff4488}));
scene.add(ico);
// <background stuff>
let backScene = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), new THREE.MeshBasicMaterial({
color: 0x004488,
onBeforeCompile: shader => {
shader.uniforms.time = gu.time;
shader.uniforms.resolution = gu.resolution;
shader.vertexShader = `
varying vec2 vUv;
` + shader.vertexShader;
shader.vertexShader = shader.vertexShader.replace(
`#include <uv_vertex>`,
`vUv = uv;`
);
shader.fragmentShader = `
${noise}
uniform float time;
uniform vec2 resolution;
varying vec2 vUv;
` + shader.fragmentShader;
shader.fragmentShader = shader.fragmentShader.replace(
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`
vec2 tUv = vUv * 5.0;
tUv.x *= resolution.x / resolution.y;
float t = time * 0.5;
tUv -= vec2(t * 0.25, t);
float n1 = snoise(vec3(tUv, time * 0.125));
n1 = (n1 + 1.0) * 0.5;
float n2 = snoise(vec3(n1 * 10., 1, 1));
n2 = sin(((n2 + 1.0) * 0.5) * 3.1415926 * 2.);
float effect = smoothstep(0.1, 0.125, n1) * (1. - smoothstep(0.375, 0.4, n2));
float coef = sin(n2 * 3.141526 * 0.5) * 0.125;
float e = effect - abs(coef);
e = n1 > 0.25 && n1 < 0.75? e * e : pow(e, 16.);
vec4 diffuseColor = vec4(diffuse * e, opacity);
`
);
}
}));
let backCamera = new THREE.Camera();
let backRT = new THREE.WebGLRenderTarget(innerWidth, innerHeight);
scene.background = backRT.texture; // assign to background
// </background stuff>
let clock = new THREE.Clock();
renderer.setAnimationLoop( _ => {
let t = clock.getElapsedTime();
gu.time.value = t;
ico.rotation.x = t * 0.31;
ico.rotation.y = t * 0.27;
renderer.setRenderTarget(backRT); // set render target
renderer.render(backScene, backCamera); //render background
renderer.setRenderTarget(null); // remove render target, render to default buffer
renderer.render(scene, camera); // render scene
renderer.render(scene, camera);
})
body{
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script>
var noise = `
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
// noise functions.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : stegu
// 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
// https://github.com/stegu/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) ) );
}
`;
</script>
I am trying to preserve the proportions at least a little when the window is resized to smaller sizes at the CodePen here. Currently it turns out to be really hard to see the lines and the interaction on mobile. Do you have a solution for this? Maybe it makes sense to double the scale on resize based on the window but I am a bit lost on how I can implement it.
The responsible part of the JS:
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize(event) {
container.style.height = window.innerHeight+"px";
container.style.width = window.innerWidth+"px";
canvasWidth = container.offsetWidth;
canvasHeight = container.offsetHeight;
//send new size value to the shader and resize the window
uniforms.resolution.value.x = canvasWidth;
uniforms.resolution.value.y = canvasHeight;
//var res = canvasWidth / cols;
//rows = canvasHeight / res;
uniforms.colsrows.value.x = cols;
uniforms.colsrows.value.y = rows;//rows;
renderer.setSize(canvasWidth, canvasHeight);
}
Here is the pen:
//Create var for the contenair, the webGL 3D scene, uniforms to bind into shader and timer
var container;
var camera, scene, renderer;
var uniforms;
var startTime;
var cols = 50.;
var rows = 50.0;
init(); //init scene
animate(); //updateScene
function init() {
//get contenaire
container = document.getElementById('container');
//Create THREE.JS scene and timer
startTime = Date.now();
camera = new THREE.Camera();
camera.position.z = 1;
scene = new THREE.Scene();
//create a simple plance
var geometry = new THREE.PlaneBufferGeometry(16, 9);
//create uniform table which provide all our GLSL binding
uniforms = {
time: { type: "f", value: 1.0 },
resolution: { type: "v2", value: new THREE.Vector2() },
colsrows: {type: "v2", value: new THREE.Vector2()},
mouse: {type: "v2", value: new THREE.Vector2()}
};
//create THREE.JS material
var material = new THREE.ShaderMaterial( {
//set shaders and uniforms into material
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
} );
//create mesh, add it to the scene
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
//create renderer and add it to the DOM
renderer = new THREE.WebGLRenderer();
container.appendChild(renderer.domElement);
//check window for resize This will give us the proper resolution values to bind
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize(event) {
container.style.height = window.innerHeight+"px";
container.style.width = window.innerWidth+"px";
canvasWidth = container.offsetWidth;
canvasHeight = container.offsetHeight;
//send new size value to the shader and resize the window
uniforms.resolution.value.x = canvasWidth;
uniforms.resolution.value.y = canvasHeight;
//var res = canvasWidth / cols;
//rows = canvasHeight / res;
uniforms.colsrows.value.x = cols;
uniforms.colsrows.value.y = rows;//rows;
renderer.setSize(canvasWidth, canvasHeight);
}
function animate() {
render();
requestAnimationFrame(animate);
}
function render() {
var currentTime = Date.now();
var elaspedSeconds = (currentTime - startTime) / 1000.0;
var maxTime = 4.0;
var normTime = (elaspedSeconds % maxTime) / maxTime;
uniforms.time.value = elaspedSeconds * 1.0;
renderer.render(scene, camera);
}
function move(ev){
mx = ev.clientX
my = ev.clientY;
// console.log(mx+" , "+my);
uniforms.mouse.value.x = mx;
uniforms.mouse.value.y = my;
}
document.addEventListener('mousemove', move)
html, body {
margin: 0;
height: 100%;
background : #1a1a1a;
}
canvas {
display: block;
cursor: none;
}
#container{
background : black;
color : white;
margin: auto;
width : 500px;
height : 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<div id="container"></div>
<!-- GLSL SCRIPT -->
<!-- vertex shader -->
<script id="vertexShader" type="x-shader/x-vertex">
void main() {
gl_Position = vec4(position, 1.0);
}
</script>
<!-- fragment shader -->
<script id="fragmentShader" type="x-shader/x-fragment">
#define TWO_PI 6.28318530718
#define EPSILON 0.000011
uniform vec2 resolution;
uniform float time;
uniform vec2 colsrows;
uniform vec2 mouse;
float HueToRGB(float f1, float f2, float hue)
{
if (hue < 0.0)
hue += 1.0;
else if (hue > 1.0)
hue -= 1.0;
float res;
if ((6.0 * hue) < 1.0)
res = f1 + (f2 - f1) * 6.0 * hue;
else if ((2.0 * hue) < 1.0)
res = f2;
else if ((3.0 * hue) < 2.0)
res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
else
res = f1;
return res;
}
vec3 HSLToRGB(vec3 hsl)
{
vec3 rgb;
if (hsl.y == 0.0)
rgb = vec3(hsl.z); // Luminance
else
{
float f2;
if (hsl.z < 0.5)
f2 = hsl.z * (1.0 + hsl.y);
else
f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);
float f1 = 2.0 * hsl.z - f2;
rgb.r = HueToRGB(f1, f2, hsl.x + (1.0/3.0));
rgb.g = HueToRGB(f1, f2, hsl.x);
rgb.b= HueToRGB(f1, f2, hsl.x - (1.0/3.0));
}
return rgb;
}
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
vec2 rotateFrom(vec2 uv, vec2 center, float angle){
vec2 uv_ = uv - center;
uv_ = rotate2d(angle) * uv_;
uv_ = uv_ + center;
return uv_;
}
float random(float value){
return fract(sin(value) * 43758.5453123);
}
float random(vec2 tex){
return fract(sin(dot(tex.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
vec2 random2D(vec2 uv){
uv = vec2(dot(uv, vec2(127.1, 311.7)), dot(uv, vec2(269.5, 183.3)));
//return -1.0 + 2.0 * fract(sin(uv) * 43758.5453123);
return fract(sin(uv) * 43758.5453123); //return without offset on x, y
}
vec3 random3D(vec3 uv){
uv = vec3(dot(uv, vec3(127.1, 311.7, 120.9898)), dot(uv, vec3(269.5, 183.3, 150.457)), dot(uv, vec3(380.5, 182.3, 170.457)));
return -1.0 + 2.0 * fract(sin(uv) * 43758.5453123);
}
float cubicCurve(float value){
return value * value * (3.0 - 2.0 * value); // custom cubic curve
}
vec2 cubicCurve(vec2 value){
return value * value * (3.0 - 2.0 * value); // custom cubic curve
}
vec3 cubicCurve(vec3 value){
return value * value * (3.0 - 2.0 * value); // custom cubic curve
}
float noise(vec2 uv){
vec2 iuv = floor(uv);
vec2 fuv = fract(uv);
vec2 suv = cubicCurve(fuv);
float dotAA_ = dot(random2D(iuv + vec2(0.0)), fuv - vec2(0.0));
float dotBB_ = dot(random2D(iuv + vec2(1.0, 0.0)), fuv - vec2(1.0, 0.0));
float dotCC_ = dot(random2D(iuv + vec2(0.0, 1.0)), fuv - vec2(0.0, 1.0));
float dotDD_ = dot(random2D(iuv + vec2(1.0, 1.0)), fuv - vec2(1.0, 1.0));
return mix(
mix(dotAA_, dotBB_, suv.x),
mix(dotCC_, dotDD_, suv.x),
suv.y);
}
float noise(vec3 uv){
vec3 iuv = floor(uv);
vec3 fuv = fract(uv);
vec3 suv = cubicCurve(fuv);
float dotAA_ = dot(random3D(iuv + vec3(0.0)), fuv - vec3(0.0));
float dotBB_ = dot(random3D(iuv + vec3(1.0, 0.0, 0.0)), fuv - vec3(1.0, 0.0, 0.0));
float dotCC_ = dot(random3D(iuv + vec3(0.0, 1.0, 0.0)), fuv - vec3(0.0, 1.0, 0.0));
float dotDD_ = dot(random3D(iuv + vec3(1.0, 1.0, 0.0)), fuv - vec3(1.0, 1.0, 0.0));
float dotEE_ = dot(random3D(iuv + vec3(0.0, 0.0, 1.0)), fuv - vec3(0.0, 0.0, 1.0));
float dotFF_ = dot(random3D(iuv + vec3(1.0, 0.0, 1.0)), fuv - vec3(1.0, 0.0, 1.0));
float dotGG_ = dot(random3D(iuv + vec3(0.0, 1.0, 1.0)), fuv - vec3(0.0, 1.0, 1.0));
float dotHH_ = dot(random3D(iuv + vec3(1.0, 1.0, 1.0)), fuv - vec3(1.0, 1.0, 1.0));
float passH0 = mix(
mix(dotAA_, dotBB_, suv.x),
mix(dotCC_, dotDD_, suv.x),
suv.y);
float passH1 = mix(
mix(dotEE_, dotFF_, suv.x),
mix(dotGG_, dotHH_, suv.x),
suv.y);
return mix(passH0, passH1, suv.z);
}
float drawLine(vec2 uv, vec2 p1, vec2 p2, float r)
{
//from https://www.shadertoy.com/view/MtlSDr
vec2 l = p2 - p1;
float L = length(l);
float L2 = L*L;
float d1 = length(uv - p1);
float d2 = length(uv - p2);
float d = min(d1, d2);
float ds = dot(uv - p1, l);
if (ds >= 0.0 && ds <= L2)
{
vec2 n = vec2(-l.y, l.x) / L;
d = min(d, abs(dot(uv - p1, n)));
}
return 1.0 - smoothstep(0.0, 0.01, d - r);
}
vec2 fishey(vec2 uv, vec2 center, float ratio, float dist){
vec2 puv = uv + vec2(1.0);
//center coords
vec2 m = vec2(center.x, center.y/ratio) + vec2(1.0);
//vector from center to current fragment
vec2 d = puv - m;
// distance of pixel from center
float r = sqrt(dot(d, d));
//amount of effect
float power = ( TWO_PI / (2.0 * sqrt(dot(m, m)))) * mix(0.1, 0.4, pow(dist, 0.75));
//radius of 1:1 effect
float bind;
if (power > 0.0) bind = sqrt(dot(m, m));//stick to corners
//else {if (ratio < 1.0) bind = m.x; else bind = m.y;}//stick to borders
//Weird formulas
vec2 nuv;
if (power > 0.0)//fisheye
nuv = m + normalize(d) * tan(r * power) * bind / tan( bind * power);
else if (power < 0.0)//antifisheye
nuv = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
else
nuv = puv;//no effect for power = 1.0
return nuv - vec2(1.0);
}
vec4 addGrain(vec2 uv, float time, float grainIntensity){
float grain = random(fract(uv * time)) * grainIntensity;
return vec4(vec3(grain), 1.0);
}
void main(){
vec2 ouv = gl_FragCoord.xy / resolution.xy;
vec2 uv = ouv;
float ratio = resolution.x / resolution.y;
vec2 nmouse = vec2(mouse.x, mouse.y) / resolution.xy;
nmouse.y = 1.0 - nmouse.y;
float maxDist = 0.35;
float blurEdge = maxDist * 0.5;
float blurEdge2 = maxDist * 1.0;
vec2 mouseToUV = (uv - nmouse) / vec2(1.0, ratio);
float mouseDistance = 1.0 - smoothstep(maxDist - blurEdge, maxDist, length(mouseToUV));
float mouseDistance2 = 1.0 - smoothstep(maxDist - blurEdge2, maxDist, length(mouseToUV));
uv = fishey(uv, nmouse, ratio, mouseDistance2);
uv = rotateFrom(uv, vec2(0.5), time * 0.1);
//animate y
//wave
uv.y /= ratio;
vec2 basedUV = uv + vec2(1.0);
float complexityX = 10.0;
float complexityY = 10.0;
float maxAmp = mix(0.05, 0.75, mouseDistance);
float amp = 0.01 * mouseDistance + noise(vec3(basedUV.x * complexityX, basedUV.y * complexityY, time * 0.1)) * maxAmp;
float theta = time + mouseDistance + basedUV.y * (TWO_PI);
uv.x = fract(uv.x + sin(theta) * amp);
//divide into cols rows
vec2 nuv = uv * colsrows;
vec2 fuv = fract(nuv);
vec2 iuv = floor(nuv);
float minSpeed = 1.0;
float maxSpeed = 5.0;
float speed = minSpeed + random(floor(uv.x * colsrows.x)) * (maxSpeed - minSpeed);
fuv.y = fract(fuv.y + time * speed);
//draw dash line
float minWeight = 0.005 + random(vec2(iuv.x, 0.0)) * 0.05;
float strokeWeight = mix(minWeight, minWeight * 5.0, mouseDistance);
float dlineWidth = mix(1.0, 0.25 - strokeWeight, mouseDistance);//0.5 - strokeWeight;
float dline = drawLine(fuv, vec2(0.5, 0.5 - dlineWidth * 0.5), vec2(0.5, 0.5 + dlineWidth * 0.5), strokeWeight);
float randIndexHue = random(vec2(iuv.x + floor(time), 0.0));
float noiseHue = noise(vec3(randIndexHue, randIndexHue, time));
float hue = mix(0.111, 0.138, randIndexHue + (noiseHue * 0.5));
vec4 grain = addGrain(ouv, time, 0.15);
//vec3 color = HSLToRGB(vec3(hue, 1.0, 0.5));
//vec3 bgColor = HSLToRGB(vec3(0.772, mix(0.75, 1.0, mouseDistance), mix(0.1, 0.25, mouseDistance)));
vec3 color = vec3(1.0);
vec3 bgColor = vec3(0.0);
float val = dline * (mouseDistance * 0.5 + 0.5);
vec3 albedo = mix(bgColor, color, val);
gl_FragColor = vec4(albedo, 1.0) + grain;
}
</script>
The thickness of the lines is computed in drawLine and depends on the parameters to smoothstep:
float drawLine(vec2 uv, vec2 p1, vec2 p2, float r)
{
// [...]
return 1.0 - smoothstep(0.0, 0.01, d - r);
}
Increase the parameter to edge1, to generate "thicker" lines (e.g. 0.1):
float drawLine(vec2 uv, vec2 p1, vec2 p2, float r)
{
// [...]
return 1.0 - smoothstep(0.0, 0.1, d - r);
}
You can add an additional uniform variable for the line:
uniform float thickness;
float drawLine(vec2 uv, vec2 p1, vec2 p2, float r)
{
// [...]
return 1.0 - smoothstep(0.0, thickness, d - r);
}
uniforms = {
// [...]
thickness: { type: "f", value: 0.1 },
};
I am lately experimenting and learning webgl and shaders, as i am trying to build animation for the website background. I was able to port simple examples from shadertoy into three.js to play with it. Now i am trying to better understand more advanced examples and i am struggling with this particlar example:
https://www.shadertoy.com/view/4sfBWj
I am aware that to port shader program to three.js you need to:
create three.js basic setup with new THREE.PlaneGeometry()
create uniforms for iTime and iResolution
create vertex and fragment shaders script tags
populate fragment shader with shadertoy contents (image section)
populate vertex shader with generic script
change names to gl_FragColor and gl_FragCoord
change name of the function to void main(void)
if there is some texture used in one or more channels then
load texture with new THREE.TextureLoader() and create uniform for iChannel0
Basic examples will be good to go with above. However the one i linked has:
Buffer A
Buffer B
and both of these also include shader programs and main functions that runs them, how do i deal with it to be able to port it to three.js?
my current progress:
var container;
var camera, scene0, scene1, scene2, renderer;
var uniforms0, uniforms1, uniforms2;
var startTime;
var renderTarget0, renderTarget1;
var clock = new THREE.Clock();
init();
animate();
function init() {
container = document.getElementById( 'container' );
startTime = Date.now();
camera = new THREE.Camera();
camera.position.z = 1;
scene0 = new THREE.Scene();
scene1 = new THREE.Scene();
scene2 = new THREE.Scene();
renderTarget0 = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
renderTarget1 = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
/* scene0 */
var geometry0 = new THREE.PlaneGeometry(700, 394, 1, 1);
uniforms0 = {
iTime: { type: "f", value: 1.0 },
iResolution: { type: "v1", value: new THREE.Vector2(), }
};
var material0 = new THREE.ShaderMaterial( {
uniforms: uniforms0,
vertexShader: document.getElementById( 'vs0' ).textContent,
fragmentShader: document.getElementById( 'fs0' ).textContent
});
/* scene0 */
var mesh0 = new THREE.Mesh( geometry0, material0 );
/* scene1 */
var geometry1 = new THREE.PlaneGeometry(700, 394, 1, 1);
uniforms1 = {
iTime: { type: "f", value: 1.0 },
iResolution: { type: "v1", value: new THREE.Vector2(), }
};
var material1 = new THREE.ShaderMaterial( {
uniforms: uniforms1,
vertexShader: document.getElementById( 'vs1' ).textContent,
fragmentShader: document.getElementById( 'fs1' ).textContent,
iChannel0: {type: 't', value: renderTarget0 }
});
var mesh1 = new THREE.Mesh( geometry1, material1 );
/* scene1 */
/* scene2 */
var geometry2 = new THREE.PlaneGeometry(700, 394, 1, 1);
uniforms2 = {
iTime: { type: "f", value: 1.0 },
iResolution: { type: "v1", value: new THREE.Vector2(), }
};
var material2 = new THREE.ShaderMaterial( {
uniforms: uniforms1,
vertexShader: document.getElementById( 'vs2' ).textContent,
fragmentShader: document.getElementById( 'fs2' ).textContent,
iChannel0: {type: 't', value: renderTarget0 },
iChannel1: {type: 't', value: renderTarget1 }
});
var mesh2 = new THREE.Mesh( geometry2, material2 );
/* scene2 */
scene0.add( mesh0 );
scene1.add( mesh1 );
scene2.add( mesh2 );
renderer = new THREE.WebGLRenderer();
container.appendChild( renderer.domElement );
onWindowResize();
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize( event ) {
uniforms0.iResolution.value.x = window.innerWidth;
uniforms0.iResolution.value.y = window.innerHeight;
uniforms1.iResolution.value.x = window.innerWidth;
uniforms1.iResolution.value.y = window.innerHeight;
uniforms2.iResolution.value.x = window.innerWidth;
uniforms2.iResolution.value.y = window.innerHeight;
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
//renderer.render(scene0, camera, renderTarget0);
//renderer.render(scene1, camera, renderTarget1);
uniforms1.iChannel0.value = rendertarget0.texture;
uniforms2.iChannel0.value = rendertarget0.texture;
uniforms2.iChannel1.value = rendertarget1.texture;
uniforms0.iTime.value += clock.getDelta();
uniforms1.iTime.value += clock.getDelta();
uniforms2.iTime.value += clock.getDelta();
//renderer.render( scene2, camera );
}
body {
margin:0;
padding:0;
overflow:hidden;
}
<script src="//threejs.org/build/three.min.js"></script>
<div id="container"></div>
<!-- BREAK --->
<script id="vs0" type="x-shader/x-vertex">
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script id="fs0" type="x-shader/x-fragment">
uniform vec2 iResolution;
uniform float iTime;
const mat2 m = mat2( 0.8, 0.6, -0.6, 0.8 );
const mat3 m3 = mat3( 0.8, 0.6, 0.0, -0.6, 0.80, 0.0, 0.0, 0.0, 1.0) *
mat3( 1.0, 0.0, 0.0, 0.0, -0.60, 0.80, 0.0, 0.8, 0.6) *
mat3( 0.8, 0.6, 0.0, -0.6, 0.80, 0.0, 0.0, 0.0, 1.0) *
mat3( 1.0, 0.0, 0.0, 0.0, -0.60, 0.80, 0.0, 0.8, 0.6);
float time;
float n1f0(float p) {
return fract(sin(p * 1.7227636) * 8.03e2);
}
float n1f1(float p) {
return fract(sin(p * 1.42736 + 1.12) * 5.1e2);
}
float n1f2(float p) {
return fract(sin(p * 1.22712 + 12.161) * 5.2e2);
}
float n3f(vec3 p) {
return fract(n1f0(p.x) + n1f1(p.y) + n1f2(p.z) + n1f0(p.x * 1.613) + n1f1(p.y * 3.112) + n1f2(p.z * 4.112));
}
float n3(vec3 p) {
vec3 b = floor(p);
vec3 e = b + vec3(1.0);
vec3 f = smoothstep(vec3(0.0), vec3(1.0), fract(p));
float c000 = n3f(b);
float c001 = n3f(vec3(b.x, b.y, e.z));
float c010 = n3f(vec3(b.x, e.y, b.z));
float c011 = n3f(vec3(b.x, e.y, e.z));
float c100 = n3f(vec3(e.x, b.y, b.z));
float c101 = n3f(vec3(e.x, b.y, e.z));
float c110 = n3f(vec3(e.x, e.y, b.z));
float c111 = n3f(e);
vec4 z = mix(vec4(c000, c100, c010, c110), vec4(c001, c101, c011, c111), f.z);
vec2 yz = mix(z.xy, z.zw, f.y);
return mix(yz.x, yz.y, f.x);
}
float fbm4( vec3 p )
{
float f = 0.0;
p = m3 * p;
f += 0.5000*n3( p ); p = m3*p*2.02;
f += 0.2500*n3( p ); p = m3*p*2.03;
f += 0.1250*n3( p ); p = m3*p*2.01;
f += 0.0625*n3( p );
return f/0.9375;
}
float fbm4( vec2 p )
{
return fbm4(vec3(p, time));
}
float fbm6( vec3 p )
{
float f = 0.0;
p = m3 * p;
f += 0.500000*n3( p ); p = m3*p*2.02;
f += 0.250000*n3( p ); p = m3*p*2.03;
f += 0.125000*n3( p ); p = m3*p*2.01;
f += 0.062500*n3( p ); p = m3*p*2.04;
f += 0.031250*n3( p ); p = m3*p*2.01;
f += 0.015625*n3( p );
return f/0.984375;
}
float fbm6( vec2 p )
{
return fbm6(vec3(p, time));
}
float grid(vec2 p) {
p = sin(p * 3.1415);
return smoothstep(-0.01, 0.01, p.x * p.y);
}
void main(void) {
time = iTime * 0.7;
vec2 q = gl_FragCoord.xy / iResolution.xy;
vec2 p = -1.0 + 2.0 * q;
p.x *= iResolution.x/iResolution.y;
p.y *= 0.3;
p.y -= time * 1.5;
float tc = time * 1.2;
float tw1 = time * 2.5;
float tw2 = time * 0.6;
vec3 vw1 = vec3(p, tw1);
vw1.y *= 2.8;
vec2 ofs1 = vec2(fbm4(vw1), fbm4(vw1 + vec3(10.0, 20.0, 50.0)));
ofs1.y *= 0.3;
ofs1.x *= 1.3;
vec3 vw2 = vec3(p, tw2);
vw2.y *= 0.8;
vec2 ofs2 = vec2(fbm4(vw2), fbm4(vw2 + vec3(10.0, 20.0, 50.0)));
ofs2.y *= 0.3;
ofs2.x *= 1.3;
vec2 vs = (p + ofs1 * 0.5 + ofs2 * 0.9) * 4.0;
vec3 vc = vec3(vs, tc);
float l;
l = fbm6(vc);
l = smoothstep(0.0, 1.0, l);
l = max(0.0, (l - pow(q.y * 0.8, 0.6)) * 1.8);
float r = pow(l , 1.5);
float g = pow(l , 3.0);
float b = pow(l , 6.0);
//r = grid(vs);
gl_FragColor = vec4( r, g, b, 1.0 );
}
</script>
<!-- BREAK --->
<script id="vs1" type="x-shader/x-vertex">
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script id="fs1" type="x-shader/x-fragment">
uniform vec2 iResolution;
uniform float iTime;
uniform sampler2D iChannel0;
#ifdef GL_ES
precision mediump float;
#endif
#define SIGMA 5.0
float normpdf(in float x, in float sigma)
{
return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma;
}
void main(void) {
vec3 c = texture2D(iChannel0, gl_FragCoord.xy / iResolution.xy).rgb;
//declare stuff
const int mSize = int(SIGMA * 11.0/7.0);
const int kSize = (mSize-1)/2;
float kernel[mSize];
vec3 finalColor = vec3(0.0);
//create the 1-D kernel
float sigma = SIGMA;
float Z = 0.0;
for (int j = 0; j <= kSize; ++j)
{
kernel[kSize+j] = kernel[kSize-j] = normpdf(float(j), sigma);
}
//get the normalization factor (as the gaussian has been clamped)
for (int j = 0; j < mSize; ++j)
{
Z += kernel[j];
}
//read out the texels
for (int i=-kSize; i <= kSize; ++i)
{
for (int j=-kSize; j <= kSize; ++j)
{
finalColor += kernel[kSize+j]*kernel[kSize+i]*texture2D(iChannel0, (gl_FragCoord.xy+vec2(float(i),float(j))) / iResolution.xy).rgb;
}
}
finalColor /= Z*Z;
//finalColor = c + finalColor * 0.3;
gl_FragColor = vec4(finalColor, 1.0);
}
</script>
<!-- BREAK --->
<script id="vs2" type="x-shader/x-vertex">
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script id="fs2" type="x-shader/x-fragment">
uniform vec2 iResolution;
uniform float iTime;
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
#ifdef GL_ES
precision mediump float;
#endif
#define SIGMA 5.0
float normpdf(in float x, in float sigma)
{
return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma;
}
void main(void) {
vec3 c = texture2D(iChannel0, gl_FragCoord.xy / iResolution.xy).rgb;
//gl_FragColor = vec4(c, 1.0);
//return;
//declare stuff
const int mSize = int(SIGMA * 11.0/7.0);
const int kSize = (mSize-1)/2;
float kernel[mSize];
vec3 finalColor = vec3(0.0);
//create the 1-D kernel
float sigma = SIGMA;
float Z = 0.0;
for (int j = 0; j <= kSize; ++j)
{
kernel[kSize+j] = kernel[kSize-j] = normpdf(float(j), sigma);
}
//get the normalization factor (as the gaussian has been clamped)
for (int j = 0; j < mSize; ++j)
{
Z += kernel[j];
}
//read out the texels
for (int i=-kSize; i <= kSize; ++i)
{
for (int j=-kSize; j <= kSize; ++j)
{
finalColor += kernel[kSize+j]*kernel[kSize+i]*texture2D(iChannel1, (gl_FragCoord.xy+vec2(float(i),float(j))) / iResolution.xy).rgb;
}
}
finalColor /= Z*Z;
finalColor = c + pow(finalColor, vec3(0.5)) * 0.5;
gl_FragColor = vec4(finalColor, 1.0);
}
</script>
This example uses multiple renders per frame. It works like this:
render shaderA to buffer
pass output to shaderB
render shaderB to buffer
pass output to shaderC
render shaderC to canvas
To replicate this in Three.js, you'll need a WebGLRenderTarget as intermediary to pass the output from one render as a texture to the next shader. Here's the pseudocode with only 2 renders, you'll need to extend it if you need more:
var renderer = new WebGLRenderer(w, h, ...);
var scene0 = new Scene();
var scene1 = new Scene();
var plane0 = new THREE.PlaneBufferGeometry(1, 1);
var plane1 = new THREE.PlaneBufferGeometry(1, 1);
// ... continue building materials, shaders, etc
// Add plane mesh to its corresponding scene
scene0.add(planeMesh0);
scene1.add(planeMesh1);
// You should only need one camera if they're both in the same position.
var cam = new Camera();
// renderTarget will store the first buffer
var renderTarget = new WebGLRenderTarget(w, h);
update() {
// This pass will render the first shader
// Its output will be drawn on the rendertarget's texture
renderer.render(scene0, cam, renderTarget);
// We assign the output of the first render pass to the second plane
plane1.uniforms.texture.value = rendertarget.texture;
// Now we render the second shader to the canvas
renderer.render(scene1, cam);
}
Keep in mind you have to render separate scenes in each pass to avoid recursion issues, so you'll have to add each plane to a separate scene. To learn more about WebGLRenderTarget you can read about it in the docs
I tried to create the snow effect like the one on the bottom page of this link http://blog.edankwan.com/post/my-first-christmas-experiment. Everything else works fine But just can't make the motion blur effect work. Any ideas?
the texture sprite used to achieve the motion blur effect
here is the code:
(function(global) {
var img = 'https://i.imgur.com/hlmsgWA.png'
var renderer, scene, camera
var w = 800, h = 320
var uniforms
var geometry
var texture, material
var gui
var conf = {
amount: 200,
speed: 0.5,
time: 0
}
var obj = {
init: function() {
renderer = new THREE.WebGLRenderer({
antialias: true
})
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(w, h)
camera = new THREE.Camera
scene = new THREE.Scene()
geometry = new THREE.BufferGeometry()
var positions = []
for(var i = 0, l = conf.amount; i < l; i++) {
positions[i * 3] = Math.random() * 800 - 400
positions[i * 3 + 1] = i
positions[i * 3 + 2] = Math.random() * 800
}
geometry.addAttribute('position', new THREE.Float32BufferAttribute(positions, 3))
var vs = document.getElementById('vertexShader').textContent
var fs = document.getElementById('fragmentShader').textContent
uniforms = {
u_amount: {
type: 'f',
value: conf.amount
},
u_speed: {
type: 'f',
value: conf.speed
},
u_time: {
type: 'f',
value: conf.time
},
u_resolution: {
type: 'vec2',
value: new THREE.Vector2(w, h)
},
u_texture : {
value: new THREE.TextureLoader().load(img)
}
}
material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vs,
fragmentShader: fs,
transparent: true
})
var points = new THREE.Points(geometry, material)
scene.add(points)
document.body.appendChild(renderer.domElement)
this.render()
this.createGui()
},
createGui: function() {
gui = new dat.GUI()
gui.add(conf, 'speed', -1, 1)
},
render: function() {
requestAnimationFrame(this.render.bind(this))
uniforms.u_time.value += conf.speed * 0.003
renderer.render(scene, camera)
}
}
obj.init()
})(window)
<script id="vertexShader" type="x-shader/x-vertex">
precision highp float;
vec3 getPosOffset(float ratio, float thershold) {
return vec3(
cos((ratio * 80.0 + 10.0) * thershold) * 20.0 * thershold,
(sin((ratio * 90.0 + 30.0) * thershold) + 1.0) * 5.0 * thershold + mix(500.0, -500.0, ratio / thershold),
sin((ratio * 70.0 + 20.0) * thershold) * 20.0 * thershold
);
}
uniform vec2 u_resolution;
uniform float u_amount;
uniform float u_speed;
uniform float u_time;
varying float v_alpha;
varying float v_rotation;
varying float v_index;
void main() {
float indexRatio = position.y / u_amount;
float thershold = 0.7 + indexRatio * 0.3;
float ratio = mod(u_time - indexRatio * 3.0, thershold);
float prevRatio = mod(u_time - u_speed - indexRatio * 3.0, thershold);
vec3 offsetPos = getPosOffset(ratio, thershold);
vec3 prevOffsetPos = getPosOffset(prevRatio, thershold);
vec3 pos = position;
pos += offsetPos;
float perspective = (2000.0 - pos.z) / 2000.0;
pos.x *= perspective;
pos.y *= perspective;
float delta = length(offsetPos.xy - prevOffsetPos.xy);
float maxDelta = 2.7;
v_index = floor(pow(clamp(delta, 0.0, maxDelta) / maxDelta, 5.0) * 15.99);
v_rotation = atan((offsetPos.x - prevOffsetPos.x) / (offsetPos.y - prevOffsetPos.y));
pos.x *= 2.0 / u_resolution.x;
pos.y *= 2.0 / u_resolution.y;
pos.z = 0.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
gl_PointSize = 24.0 * perspective;
v_alpha = perspective;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D u_texture;
varying float v_rotation;
varying float v_index;
varying float v_alpha;
void main() {
vec2 coord = gl_PointCoord.xy;
coord = vec2(
clamp(cos(v_rotation) * (coord.x - 0.5) + sin(v_rotation) * (coord.y - 0.5) + 0.5, 0.0, 1.0),
clamp(cos(v_rotation) * (coord.y - 0.5) - sin(v_rotation) * (coord.x - 0.5) + 0.5, 0.0, 1.0)
);
float index = floor(v_index + 0.5);
coord.y = - coord.y + 4.0;
coord.x += (index - floor(index / 4.0) * 4.0);
coord.y -= floor(index / 4.0);
coord *= 0.25;
vec4 color = texture2D(u_texture, coord);
color.a *= v_alpha;
gl_FragColor = color;
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.js"></script>
http://blog.edankwan.com/post/my-first-christmas-experiment.
In snippet code you treat variable speed differently in your update loop and in vertex shader.
Update: uniforms.u_time.value += conf.speed * 0.003
Shader usage: float prevRatio = mod(u_time - u_speed - indexRatio * 3.0, thershold);
You'll get desired result if you change u_speed to u_speed * 0.003 in shader code (or better move that multiplication to definition in javascript).
Fast way to debug such things - pass your values to output color and check if it is what you expect.
==================
If someone wants to make tails with non-flat curvature - you can store last N points of each particle path. Then you can fully compute trail mesh geometry on CPU and stream it to GPU.
Or another way: you can upload all paths to single Uniform Buffer Object and find right point to fetch with pointId and uv of tail mesh vertex.
I have a tunnel with a rotating texture and alpha map. I would like to change the opacity, eventually tweening, of the alpha map.
I start the opacity at 0 and then later go to raise it. However the live view doesn't change while the opacity property does. It is stuck on full opacity in the live view. I tried switching the needsUpdate property of the material to true but it didn't change anything.
Here is the setup...
function addTunnel(){
var cylTexture = loader.load("wormhole.jpg"),
cylAlpha = loader.load("wormholeAlpha2.jpg");
cylTexture.wrapT = THREE.RepeatWrapping;
cylTexture.wrapS = THREE.RepeatWrapping;
cylAlpha.wrapT = THREE.RepeatWrapping;
cylAlpha.wrapS = THREE.RepeatWrapping;
var cylGeom = new THREE.CylinderGeometry(5000, 5000, 50000, 32, 32, true),
cylMat = new THREE.MeshPhongMaterial({
side: THREE.BackSide,
map: cylTexture,
alphaMap: cylAlpha,
transparent: true
}),
cyl = new THREE.Mesh(cylGeom, cylMat);
cyl.name = "tunnel";
scene.add(cyl);
scene.getObjectByName("tunnel").position.z= -9000;
rotateObject(scene.getObjectByName("tunnel"), -90, 0, 0);
octree.add(scene.getObjectByName("tunnel"));
tunnel = scene.getObjectByName("tunnel");
tunnel.material.alphaMap.opacity = 0;
}
The PhongMaterial shader uses the pixel value of the alphaMap as the alpha value as seen in the source HERE.
You are not going to be able to tween this without generating the aphaMap dynamically or altering the shader.
It sounds to me like it would probably be better to create your own ShaderMaterial.
HERE is an example of a Dr Who time tunnel type shader, it has transparency (change the body background-color)
<script id="tunnelVertexShader" type="x-shader/x-vertex">
varying vec3 vPosition;
void main( void ) {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="tunnelFragmentShader" type="x-shader/x-fragment">
varying vec3 vPosition;
uniform vec3 color;
uniform vec3 noiseScale;
uniform float speed;
uniform float time;
uniform float intensity;
//
// 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
//
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0; }
float mod289(float x) {
return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) {
return mod289(((x*34.0)+1.0)*x);
}
float permute(float x) {
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float taylorInvSqrt(float r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
vec4 grad4(float j, vec4 ip)
{
const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
vec4 p,s;
p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
s = vec4(lessThan(p, vec4(0.0)));
p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;
return p;
}
// (sqrt(5) - 1)/4 = F4, used once below
#define F4 0.309016994374947451
float snoise(vec4 v)
{
const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4
0.276393202250021, // 2 * G4
0.414589803375032, // 3 * G4
-0.447213595499958); // -1 + 4 * G4
// First corner
vec4 i = floor(v + dot(v, vec4(F4)) );
vec4 x0 = v - i + dot(i, C.xxxx);
// Other corners
// Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
vec4 i0;
vec3 isX = step( x0.yzw, x0.xxx );
vec3 isYZ = step( x0.zww, x0.yyz );
// i0.x = dot( isX, vec3( 1.0 ) );
i0.x = isX.x + isX.y + isX.z;
i0.yzw = 1.0 - isX;
// i0.y += dot( isYZ.xy, vec2( 1.0 ) );
i0.y += isYZ.x + isYZ.y;
i0.zw += 1.0 - isYZ.xy;
i0.z += isYZ.z;
i0.w += 1.0 - isYZ.z;
// i0 now contains the unique values 0,1,2,3 in each channel
vec4 i3 = clamp( i0, 0.0, 1.0 );
vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );
// x0 = x0 - 0.0 + 0.0 * C.xxxx
// x1 = x0 - i1 + 1.0 * C.xxxx
// x2 = x0 - i2 + 2.0 * C.xxxx
// x3 = x0 - i3 + 3.0 * C.xxxx
// x4 = x0 - 1.0 + 4.0 * C.xxxx
vec4 x1 = x0 - i1 + C.xxxx;
vec4 x2 = x0 - i2 + C.yyyy;
vec4 x3 = x0 - i3 + C.zzzz;
vec4 x4 = x0 + C.wwww;
// Permutations
i = mod289(i);
float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);
vec4 j1 = permute( permute( permute( permute (
i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
+ i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
+ i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
+ i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));
// Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
// 7*7*6 = 294, which is close to the ring size 17*17 = 289.
vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;
vec4 p0 = grad4(j0, ip);
vec4 p1 = grad4(j1.x, ip);
vec4 p2 = grad4(j1.y, ip);
vec4 p3 = grad4(j1.z, ip);
vec4 p4 = grad4(j1.w, ip);
// 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;
p4 *= taylorInvSqrt(dot(p4,p4));
// Mix contributions from the five corners
vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);
vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0);
m0 = m0 * m0;
m1 = m1 * m1;
return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))
+ dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;
}
float turbulence( vec3 p ) {
float t = -0.5;
for (float f = 1.0 ; f <= 5.0 ; f++ ){
float power = pow( 2.0, f );
t += abs( snoise( vec4( power * p, time )));
}
return t / 5.0 * intensity;
}
void main() {
vec3 nPos = vec3(vPosition.x, vPosition.y - (speed * time), vPosition.z);
float n = turbulence(nPos / (0.0 - noiseScale));
vec3 finalColor = vec3(color.x * n, color.y * n, color.z * n);
float finalAlpha = finalColor.x + finalColor.y + finalColor.z;
gl_FragColor = vec4(finalColor, finalAlpha);
}
</script>