Three js Shader Material - Pixelated glitching when transparency is set to true - javascript

I'm playing around with shaders for the first time and using THREE.RawShaderMaterial on a few meshes.
I'm getting some strange artifacts on my very simple shaders:
Vertex shader:
precision mediump float;
precision mediump int;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec4 color;
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
Fragment shader:
precision mediump float;
precision mediump int;
varying vec3 vPosition;
void main() {
gl_FragColor.r = vPosition.x;
gl_FragColor.g = vPosition.y;
gl_FragColor.b = 1.0;
}
which I use on a whole bunch of objects which are created like so:
asdfobject = new THREE.Object3D();
scene.add(asdfobject);
var geometry = new THREE.SphereGeometry(1, 4, 4);
var material = new THREE.RawShaderMaterial({
uniforms: {
time: { type: "f", value: 1.0 }
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.DoubleSide,
transparent: true,
} );
for(var i = 0; i < 80; i++) {
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);
mesh.position.multiplyScalar(400);
mesh.rotation.set(Math.random() * 2, Math.random() * 2, Math.random() * 2);
mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 50;
asdfobject.add(mesh);
}
The colour on the objects should be completely smooth, but sometimes they look a bit "glitchy" and pixelated like shown in this fiddle:
https://jsfiddle.net/weqqv5z5/
This happens especially when resizing the window.
I cannot figure out why this happens, I just know that the effect does not happen when transparent in the material is set to false (on line 23 in the fiddle)
I haven't been able to test this on any other devices yet, so it may be a graphics card specific problem, too. I am running a 64-bit Arch Linux laptop with Intel HD 4000 graphics.
Thanks in advance for any help!

You need to set the alpha value of your fragment color.
gl_FragColor.a = 0.5;
fiddle: https://jsfiddle.net/weqqv5z5/1/
three.js r.71

Related

Three.js : Modify the UV of my texture inside a custom ShaderMaterial

I've a plane geometry and I'm creating a CustomShader material related to it. It will receive some textures as uniforms. I'd like the textures to perfectly cover my plane (like the background-size:cover css property)
I managed to do it with an utility function when I used my textures with a MeshBasicMaterial :
cover( texture, aspect ) {
var imageAspect = texture.image.width / texture.image.height;
if ( aspect < imageAspect ) {
texture.matrix.setUvTransform( 0, 0, aspect / imageAspect, 1, 0, 0.5, 0.5 );
} else {
texture.matrix.setUvTransform( 0, 0, 1, imageAspect / aspect, 0, 0.5, 0.5 );
}
}
But unfortunately since I'm using the ShaderMaterial, my "cover" function doesn't apply anymore. Am I force to do it inside my fragment shader? If so how can I manage to reproduce this behavior ?
Here's my code :
const vertexShader = `
precision highp float;
uniform mat3 uUvTransform;
varying vec2 vUv;
void main() {
vUv = ( uUvTransform * vec3( uv, 1 ) ).xy;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`;
const fragmentShader = `
precision highp float;
uniform sampler2D uText1;
varying vec2 vUv;
void main() {
vec2 xy = vUv;
vec4 color = texture2D(uText1,xy);
gl_FragColor = color;
}`;
And here's my current result :
Thanks a lot
You could simply use a custom uniform, e.g. :
uniform sampler2D uText1;
uniform vec2 uUvScale;
varying vec2 vUv;
void main() {
vec2 uv = (vUv - 0.5) * uUvScale + 0.5;
gl_FragColor = texture2D(uText1, uv);
}
And :
var imageAspect = texture.image.width / texture.image.height;
if ( aspect < imageAspect ) {
material.uniforms.uUvScale.value.set(aspect / imageAspect, 1)
} else {
material.uniforms.uUvScale.value.set(1, imageAspect / aspect)
}
The way Three.js handles texture transformations like .offset, .repeat, .rotation, .center is via a Matrix3 that gets passed as a uniform into the vertex shader. The vertex shader performs the matrix multiplication, then passes the modified UVs as a varying to the fragment shader.
You can see that uniform being declared in the uv_pars_vertex.glsl.js file
You can see the transform being applied in the uv_vertex.glsl.js file
You could copy those lines of GLSL code to your ShaderMaterial's vertex shader, and I think the texture properties will come through in the Matrix3 automatically. However, if for some reason it doesn't, you could recreate the Matrix3 by copying it from the source and passing it as a uniform manually. I don't know what your utility function looks like, so it's hard to tell how you're achieving the desired scaling.

Determining the center of the screen in a WEBGL fragment shader

I started learning WebGL a couple of weeks ago and as I am trying to learn by practice, I stumbled upon a simple example of a shader that I could implement using p5.js.
In this example, I am creating concentric circles starting from the center of the screen, using this fragment, where u_resolution and u_time are uniforms passed down from p5 script as [windowWidth, windowHeight] and respectively frameCount as below:
void main(void) {
float maxAxis = max(u_resolution.x, u_resolution.y);
vec2 uv = gl_FragCoord.xy / maxAxis;
vec2 center = u_resolution / maxAxis;
gl_FragColor = vec4(
vec3(sin(u_time + distance(uv, center) * 255.0)),
1.0);
}
Using this example, I can achieve what I want, but I cannot understand why I cannot calculate the center of the fragment using the formula:
vec2 center = vec2(u_resolution.x * 0.5, u_resolution.y * 0.5);
If I do this, then it will mess up the whole rendering.
Is there a coordinate system mismatch that I am missing, or something else?
For a better explanation, I included a snippet of the original experiment that I am doing in CodePen right here.
uv and center are in range [0.0, 1.0]. There for the center of the viewport is:
vec2 center = vec2(u_resolution.x * 0.5, u_resolution.y * 0.5);
vec2 center = 0.5 * u_resolution / maxAxis;
let myShaderIn, myShaderOut;
let isPlaying = true;
const vertexShader = document.getElementById("vert-shader").textContent;
const fragmentShaderStyleIn = document.getElementById("frag-shader-style-in")
.textContent;
function setup() {
const canvas = createCanvas(windowWidth, windowHeight, WEBGL);
// canvas.mousePressed(toggleSound);
rectMode(CENTER);
// shaders
myShaderIn = createShader(vertexShader, fragmentShaderStyleIn);
// register shaders
shader(myShaderIn);
// shapes setup
noStroke();
}
function draw() {
background(0);
drawEllipse();
}
function drawEllipse() {
myShaderIn.setUniform("u_resolution", [float(width), float(height)]);
myShaderIn.setUniform("u_time", float(frameCount));
shader(myShaderIn);
ellipse(0, 0, width/2);
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
clear();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
<!-- vertex shader -->
<script type="x-shader/x-vertex" id="vert-shader">
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
attribute vec3 aPosition;
uniform mat4 uProjectionMatrix;
uniform mat4 uModelViewMatrix;
void main() {
vec4 newPosition = vec4(aPosition, 1.0);
gl_Position = uProjectionMatrix * uModelViewMatrix * newPosition;
}
</script>
<!-- fragment shader -->
<script type="x-shader/x-fragment" id="frag-shader-style-in">
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
uniform vec2 u_resolution; // canvas size (width, height)
uniform float u_time; // time in seconds since load
void main(void) {
float maxAxis = max(u_resolution.x, u_resolution.y);
// If you want to map the pixel coordinate values to the range 0 to 1, you divide by resolution.
/*With vec4 gl_FragCoord, we know where a thread is working inside the billboard.
In this case we don't call it uniform because it will be different from thread to thread,
instead gl_FragCoord is called a varying. */
vec2 uv = gl_FragCoord.xy / maxAxis;
vec2 center = 0.5 * u_resolution / maxAxis;
gl_FragColor = vec4(
vec3(sin(u_time * 0.1 + distance(uv, center) * 255.0)),
1.0);
}
</script>

Texture not rotating with the object

I have a very simple object and a texture drown to it (using shader). Everything works great, except when rotating the object, the texture is not rotating along it, but appears to stay in the 2D space, creating the 'mask' effect below:
Texture not rotating along the object
When I use a regular material and attach texture to it, all works fine, so I'm guessing I'm doing something wrong at the vertex shader.
I load the model the following way:
var loader = new THREE.JSONLoader();
loader.load( "models/cube.json", addModelToScene );
var texture = THREE.ImageUtils.loadTexture( "images/woods.jpg" );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 1.0, 1.0 );
uniforms =
{
time:
{
type: "f",
value: 1.0
},
texture1: { type: "t", value: THREE.ImageUtils.loadTexture( "images/woods.jpg" ) }
};
function addModelToScene( geometry, materials ) {
var material = new THREE.MeshFaceMaterial( materials );
var shaderMaterial = new THREE.ShaderMaterial
(
{
vertexShader: $('#vertexshader').text(),
fragmentShader: $('#fragmentshader').text(),
uniforms: uniforms
}
);
model = new THREE.Mesh( geometry, shaderMaterial );
model.scale.set( 2.5, 2.5, 2.5 );
scene.add( model );
}
Vertex shader:
varying vec2 vUv;
#ifdef GL_ES
precision highp float;
#endif
uniform float time;
uniform sampler2D texture1;
void main()
{
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
Fragment shader:
varying vec2 vUv;
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D texture1;
uniform float time;
void main()
{
vec2 u_resolution = vec2( 1700, 1000 );
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
gl_FragColor = texture2D( texture1, uv );
}
And finally I rotate the object the following way:
model.rotation.z += 0.00013;
model.rotation.z += 0.004;
Why is the texture not one with the object, but instead stays in static position? Thanks!
That's because you should use vUv varying to address the texture, not gl_FragCoord.xy:
gl_FragColor = texture2D(texture1, vUv);
gl_FragColor.xy is just pixel's (or, to be more accurate, fragment's) coordinates on a screen (i.e., window coordinates). They don't depend on rotation (or any transformations for that matter) of your object (or the object itself). They only depend upon where the pixel currently being shaded lies on the screen.

Three.js glow shader front face not rendering in chrome, works in other browsers

I am running into an issue where I am trying to apply a custom shader material to a series of nested objects to simulate a glow effect around each node. The effect works well on my home laptop (windows 8.1 latest chrome), but my work computer does not render the frontface of the glowing object, only the backface. I have checked it against a few systems and it seems to be mostly a chrome rendering issue on windows devices.
http://i.imgur.com/uYLtoxm.gif
I have included a codepen example where I shifted the glow off to the side and you can see that in some versions it is not rendering the front group of normals. The red dots should have a glow applied to each of them that shows up on the front and back(left and right in example). Any help would be appreciated, I am stumped as to what is going on.
Here is the shader material settings
local.glowNodeMat = new THREE.ShaderMaterial(
{
uniforms:
{
"c": { type: "f", value: 0 },
"p": { type: "f", value: 5.5 },
glowColor: { type: "c", value: new THREE.Color(0xaaccff) },
viewVector: { type: "v3", value: local.camera.position }
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.FrontSide,
blending: THREE.AdditiveBlending,
transparent: true
});
http://codepen.io/sniejadlik/pen/oDarE
///////////////////
FIXED thanks to Volune. Thanks for the help!
Fixed Vertex shader
<script id="vertexShader" type="x-shader/x-vertex">
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main()
{
vec3 vNormal = normalize( normalMatrix * normal );
vec3 vNormel = normalize( normalMatrix * viewVector );
// incorrect intensity = pow( c - dot(vNormal, vNormel), p );
intensity = pow( abs(c - dot(vNormal, vNormel) ), p );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<!-- fragment shader a.k.a. pixel shader -->
<script id="fragmentShader" type="x-shader/x-vertex">
uniform vec3 glowColor;
varying float intensity;
void main()
{
vec3 glow = glowColor * intensity;
gl_FragColor = vec4( glow, 1.0 );
}
</script>
The error is in your vertex shader:
intensity = pow( c - dot(vNormal, vNormel), p );
You have c = 0 and p = 0.5. c - dot(vNormal, vNormel) may be negative (when the dot product returns a positive value), so you're trying to get the square of a negative value.
For some unknown reason, the fallback in Firefox looks like pow( abs(...), 0.5 ), while the fallback in Chrome seems to be 0.0.
Try to fix your shader like this:
intensity = pow( abs( c - dot(vNormal, vNormel) ), p );

How to apply custom shader to sprite in THREE.js

I want to be able to apply some procedural structures to faces. First task, when I faced such demand is to create billboard, on which is drawn nuclear blast in open space. I hoped to make it as a animated radial gradient and I have succeed partly.
The main thing is for each fragment shader - to have access to UV as to uniform var.
Seems like the main thing about rendering sprites - is to access to camera projection matrix in the vertex shader.
Here's example http://goo.gl/A7pY01!
Now I want to draw this onto the billboard sprite. I supposed to use THREE.Sprite for this with THREE.ShaderMaterial, but had no luck in this. It seemed, that THREE.SpriteMaterial is only good material for sprites. And after inspecting some source-code I revealed why Sprites are draw in one special way using plugins.
So, before I found myself inventing my own bicycle, I felt needness to ask people how to place my own custom shader on my own custom sprite without hacking THREE.js?
So.
After a small research and work I have considered THREE.ShaderMaterial is the best option to complete this little task. Thanks to /extras/renderers/plugins/SpritePlugin, I realized how to form and position sprites using vertex shaders. I still have some question, but I found one good solution.
To accomplish my task, firstly I create a simple plane geometry:
var geometry = new THREE.PlaneGeometry( 1, 1 );
And use it in mesh with ShaderMaterial:
uniforms = {
cur_time: {type:"f", value:1.0},
beg_time:{type:"f", value:1.0},
scale:{type: "v3", value:new THREE.Vector3()}
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true,
blending:THREE.AdditiveBlending // It looks like real blast with Additive blending!!!
} );
var mesh = new THREE.Mesh( geometry, material );
Here's my shaders:
Vertex shader:
varying vec2 vUv;
uniform vec3 scale;
void main() {
vUv = uv;
float rotation = 0.0;
vec3 alignedPosition = vec3(position.x * scale.x, position.y * scale.y, position.z*scale.z);
vec2 pos = alignedPosition.xy;
vec2 rotatedPosition;
rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;
vec4 finalPosition;
finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
finalPosition.xy += rotatedPosition;
finalPosition = projectionMatrix * finalPosition;
gl_Position = finalPosition;
}
I got vertex shader from original Sprite Plugin source code, and changed it slightly.
BTW, changing += to = makes sprite screen-sticky. This thing wasted a lot of my time.
And this is my fragment shader:
uniform float cur_time;
uniform float beg_time;
varying vec2 vUv;
void main() {
float full_time = 5000.;
float time_left = cur_time - beg_time;
float expl_step0 = 0.;
float expl_step1 = 0.3;
float expl_max = 1.;
float as0 = 0.;
float as1 = 1.;
float as2 = 0.;
float time_perc = clamp( (time_left / full_time), 0., 1. ) ;
float alphap;
alphap = mix(as0,as1, smoothstep(expl_step0, expl_step1, time_perc));
alphap = mix(alphap,as2, smoothstep(expl_step1, expl_max, time_perc));
vec2 p = vUv;
vec2 c = vec2(0.5, 0.5);
float max_g = 1.;
float dist = length(p - c) * 2. ;
float step1 = 0.;
float step2 = 0.2;
float step3 = 0.3;
vec4 color;
float a0 = 1.;
float a1 = 1.;
float a2 = 0.7;
float a3 = 0.0;
vec4 c0 = vec4(1., 1., 1., a0 * alphap);
vec4 c1 = vec4(0.9, 0.9, 1., a1 * alphap);
vec4 c2 = vec4(0.7, 0.7, 1., a2 * alphap);
vec4 c3 = vec4(0., 0., 0., 0.);
color = mix(c0, c1, smoothstep(step1, step2, dist));
color = mix(color, c2, smoothstep(step2, step3, dist));
color = mix(color, c3, smoothstep(step3, max_g, dist));
gl_FragColor = color;
}
Here's example of how to make multipoint gradient, animated by time. There's a lot to optimize and several thoughts how to make this even more beautiful.
But this one is almost what I wanted.

Categories

Resources