I have been trying to procedurally generate a sphere's surface using simplex noise, and I figured that in order to get smooth, non-distorted noise I need to map each uv pixel to an xyz coordinate. I have tried a few different algorithms, with the following being my favourite:
function convert2d3d(r1, r2, x, y) {
let z = -1 + 2 * x / r1;
let phi = 2 * Math.PI * y / r1;
let theta = Math.asin(z);
return {
x: r2 * Math.cos(theta) * Math.cos(phi),
y: r2 * Math.cos(theta) * Math.sin(phi),
z: r2 * z,
}
}
While the points generated look continuous, there is severe distortion around the texture seams, and where the texture is stretched the most:
I am aware what I am trying to do is called UV mapping, yet I'm struggling to implement it correctly. Either I get severe distortion, or ugly seams. To render the sphere I am using Three.JS MeshPhongMaterial and for the noise I am using noisejs.
Do you want something like THIS?
In the gui at the top right under scene -> geometry select the sphere.
No need to mess with UVs :)
Vertex shader from the demo linked above:
varying vec3 vPosition;
void main() {
vPosition = normalize(position);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
Fragment Shader from the demo linked above:
varying vec3 vPosition;
uniform float scale;
//
// 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) ) );
}
void main() {
float n = snoise(vPosition * scale);
gl_FragColor = vec4(1.0 * n, 1.0 * n, 1.0 * n, 1.0);
}
The above takes a scale uniform of type float.
var uniforms = {
scale: { type: "f", value: 10.0 }
};
More ShaderMaterial demos
Weighing the pros and cons of WebGL vs Textures, I decided on using the following function:
function convert2d3d(r, x, y) {
let lat = y / r * Math.PI - Math.PI / 2;
let long = x / r * 2 * Math.PI - Math.PI;
return {
x: Math.cos(lat) * Math.cos(long),
y: Math.sin(lat),
z: Math.cos(lat) * Math.sin(long),
}
}
Given a point on a square texture of size r×r, convert to lat/long, returning a 3d coordinate where the texture is mapped onto a sphere of radius 1.
I adapted the function from a blog post on inear.se, converting it to use degrees throughout. I wouldn't of managed it without 2phas answer showing me an alternative, and helping me look for what I needed. Here is what it looks like:
Right now it's ugly, but this was the first step.
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 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.
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
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>