cast shadow from partly transparent plane - three.js

Is there a possibility to cast shadow from a plane for which the texture plays a video with a chromakey shader ? My trial seams to answer by NO but I guess my shader is not adapted. The object is a simple PlabeBufferGeometry and the shader is :
vertexShader is :
varying vec2 vUv;
void main() {vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition;
}
fragmentShader is :
uniform sampler2D vidtexture;
uniform vec3 color;
varying vec2 vUv;
void main()
{ vec3 tColor = texture2D( vidtexture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor, a);}

Have you tried using the discard keyword? If your frag shader encounters that keyword, it won't render that fragment, it's as if it didn't exist. You could use this to create shadow outlines defined by your chromakey instead of always geting a square shadow.
void main(){
vec3 tColor = texture2D( vidtexture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
// Do not render pixels that are less than 10% opaque
if (a < 0.1) discard;
gl_FragColor = vec4(tColor, a);
}
This is the same approach Three.js uses for Material.alphaTest in all their built-in materials. You can see the GLSL source code for that command here.

Related

How to convert a square texture into a trapezoid texture with progressive distortion in GLSL

Im in a Three.js project and Im trying to convert a square with a square texture inside into a trapezoid.
I manage to create the shape but the texture inside, although it fits/cover the shape it do it with an undesired distorsiĆ³n.
Im using a PlaneBufferGeometry with ShaderMaterial and im trying to obtain this distorsion in the shader part (although it would be ok if it is done in the threejs geometry part).
This is my vertex:
uniform sampler2D uTexture;
varying vec2 vUv;
void main(){
float scaleTOP = 0.5;
float scaleBOTTOM = 1.0;
float scaleLEFT = 1.0;
float scaleRIGHT = 1.0;
float scaleX = mix(scaleBOTTOM, scaleTOP, uv.y);
float posX = position.x*scaleX;
float scaleY = mix(scaleLEFT, scaleRIGHT, uv.x);
float posY = position.y*scaleY;
vec3 finalPosition = vec3(posX, posY);
gl_Position = projectionMatrix * modelViewMatrix * vec4( finalPosition, 1.0 );
// Varyings:
vUv = uv;
}
And this is my fragment:
uniform sampler2D uTexture;
varying vec2 vUv;
void main() {
vec4 tex = texture2D ( uTexture, vUv );
gl_FragColor = vec4(tex.r, tex.g, tex.b, 1.0);
}
Unfortunately I manage to distort the square into the trapezoid but the texture is not distorted in the way I want. See figure to see the intended result:
Figure:
My vertex and fragment were ok.
The problem was that the Threejs geometry I was using had only 2 polygons. I was using:
this.bg_geometry = new THREE.PlaneBufferGeometry(width, height, 1, 1)
Thats it... with only one division which only created two triangles which actually can be seen in the figure I posted.
I changed the geometry to:
this.bg_geometry = new THREE.PlaneBufferGeometry(width, height, 100, 100)
...and now the texture is distorted as desired.
Anyway many thanks to #prisoner849 as he put me in the track to pass 4 points as uniforms uPoints in this order: TL,TR,BL,BR to set the shape of the plane.
My vertex shader looks now like this:
uniform vec3 uPoints[4];
varying vec2 vUv;
void main(){
vec3 baselineBottom = (uPoints[3] - uPoints[2]) * uv.x + uPoints[2];
vec3 baselineTop = (uPoints[1] - uPoints[0]) * uv.x + uPoints[0];
vec3 finalPosition = (baselineTop - baselineBottom) * uv.y + baselineBottom;
gl_Position = projectionMatrix * modelViewMatrix * vec4( finalPosition, 1.0 );
vUv = uv;
}

Three.js renders unprocessed png image for texture

With three.js, I am trying to create the scene where a plane becomes transparent as the camera moves away from it.
And I textured the plane object with the round map tile which is edited from the square image below.
When I load the round image through ShaderMaterial the texture appears square like the original image.
The weird thing is it is rendered as intended when the image is loaded onto regular mesh material.
Could you tell me why three.js behaves this way? Also, how might I render round tile using shader while keeping its functionality to fade based on distance?
the full code is available here: https://codesandbox.io/s/tile-with-shader-7kw5v?file=/src/index.js
Here is an option, that takes in count only x and z coords of the plane and the camera.
vertex.glsl:
varying vec4 vPosition;
varying vec2 vUv;
void main() {
vPosition = modelMatrix * vec4(position, 1.);
vUv = uv;
gl_Position = projectionMatrix * viewMatrix * vPosition;
}
frag.glsl:
uniform vec3 u_color;
uniform vec3 u_camera;
uniform vec3 u_plane;
uniform float u_rad;
uniform sampler2D u_texture;
varying vec4 vPosition;
varying vec2 vUv;
void main() {
vec4 textureCol = texture2D(u_texture, vUv);
float rad = distance(vPosition.xz, u_camera.xz); // xz-plane
textureCol.a = 1.0 - step(1., rad / u_rad);
gl_FragColor = textureCol;
}
and u_rad uniform is
u_rad: { value: 50 },

Material shader smooth gradient between two colors

I have the following shaders used in a custom material shader in threeJS and applied to metaballs. Positions of each metaballs (2 in this example) are passed to an array allPos and they are compared in distance with vertex normal to find the closest one. Its index is used to assign a color blobColor which is then passed to the fragment shader.
vertex shader
uniform vec3 colChoice[2];
varying vec3 vNormal;
varying vec3 blobColor;
varying vec3 otherColor;
uniform vec3 allPos[2];
varying float mixScale;
void main() {
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
vNormal = normalize( normalMatrix * normal );
float prevdist = 1000000000000000000000000000000.0;
for(int i=0;i<2;i++){
float distV = distance(allPos[i],normal.xyz);
if(distV<prevdist){
prevdist = distV;
mixScale = distV;
blobColor = colChoice[i];
otherColor = colChoice[i-1];
}
}
gl_Position = projectionMatrix * mvPosition;
}
fragment shader
"varying vec3 blobColor;",
"varying vec3 otherColor;",
"varying vec3 vNormal;",
"varying float mixScale;",
void main() {
finalColor = (0.45*vNormal) + mix(otherColor,blobColor,mixScale);
gl_FragColor = vec4( finalColor, 1.0 );
}
which gives me something like this:
It is a bit rough for now and I would like to apply a dot gradient between
the two colors. Any suggestion?

Partially transparent shader occluding objects in THREE.js

I am making a game with a fog of war layer covering the board. I want to have a cursor that shows up when the player mouses over a tile, and I'm implementing this with a glow effect around the tile, also implemented using a shader.
I'm running into a strange issue: the glow effect works fine for positive x values (when the camera is set at x = -250, y = 250) but I can't see it for negative x values unless the camera gets rotated to almost completely vertical (or I move the camera underneath the fog of war layer).
It's hard to explain, so I've made a CodePen demonstrating the problem: https://codepen.io/jakedluhy/pen/QqzajN?editors=0010
I'm pretty new to custom shaders, so any insight or help would be appreciated. Here's the shaders for the fog of war:
// Vertex
varying vec4 vColor;
void main() {
vec3 cRel = cameraPosition - position;
float dx = (20.0 * cRel.x) / cRel.y;
float dz = (20.0 * cRel.z) / cRel.y;
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(
position.x + dx,
position.y,
position.z + dz,
1.0
);
vColor = vec4(0.0, 0.0, 0.0, 0.7);
}
// Fragment
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
And the shaders for the "glow":
// Vertex
varying vec4 vColor;
attribute float alpha;
void main() {
vColor = vec4(color, alpha);
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position, 1.0);
}
// Fragment
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
The math in the vertex shader for the fog of war is to keep the fog in a relative position to the game board.
Tagging THREE.js and glsl because I'm not sure whether this is a THREE.js exclusive problem or not...
Edit: version 0.87.1
Your example looks pretty weird. By setting depthWrite:false on your fog material the two boxes render.
version 0.87.1

Slow memory climb until crash in the GPU

I'm displaying a grid of particle clouds using shaders. Every time a user clicks a cloud, that cloud disappears and a new one takes its place. The curious thing is that the memory usage in the GPU climbs every time a new cloud replaces an old one - regardless of whether that new cloud is larger or smaller (and the buffer sizes always stay the same - the unused points are simply displayed offscreen with no color). After less than 10 clicks the GPU maxes out and crashes.
Here is my physics shader where the new positions are updated - I pass in the new position values for the new cloud by updating certain values in the the tOffsets texture. After that are my two (vert and frag) visual effects shaders. Can you see my efficiency issue? Or could this be a garbage collection matter? - Thanks in advance!
Physics Shader (frag only):
// Physics shader: This shader handles the calculations to move the various points. The position values are rendered out to at texture that is passed to the next pair of shaders that add the sprites and opacity.
// the tPositions sampler is added to this shader by Three.js's GPUCompute script
uniform sampler2D tOffsets;
uniform sampler2D tGridPositionsAndSeeds;
uniform sampler2D tSelectionFactors;
uniform float uPerMotifBufferDimension;
uniform float uTime;
uniform float uXOffW;
...noise functions omitted for brevity...
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec4 offsets = texture2D( tOffsets, uv ).xyzw;
float alphaMass = offsets.z;
float cellIndex = offsets.w;
if (cellIndex >= 0.0) { // this point will be rendered on screen
float damping = 0.98;
float texelSize = 1.0 / uPerMotifBufferDimension;
vec2 perMotifUV = vec2( mod(cellIndex, uPerMotifBufferDimension)*texelSize, floor(cellIndex / uPerMotifBufferDimension)*texelSize );
perMotifUV += vec2(0.5*texelSize);
vec4 selectionFactors = texture2D( tSelectionFactors, perMotifUV ).xyzw;
float swapState = selectionFactors.x;
vec4 gridPosition = texture2D( tGridPositionsAndSeeds, perMotifUV ).xyzw;
vec2 noiseSeed = gridPosition.zw;
vec4 nowPos;
vec2 velocity;
nowPos = texture2D( tPositions, uv ).xyzw;
velocity = vec2(nowPos.z, nowPos.w);
if ( swapState == 0.0 ) { // if no new position values are ready to be swapped in for this point
nowPos = texture2D( tPositions, uv ).xyzw;
velocity = vec2(nowPos.z, nowPos.w);
} else { // if swapState == 1, this means new position values are ready to be swapped in for this point
nowPos = vec4( -(uTime) + offsets.x, offsets.y, 0.0, 0.0 );
velocity = vec2(0.0, 0.0);
}
...physics calculations omitted for brevity...
vec2 newPosition = vec2(nowPos.x - velocity.x, nowPos.y - velocity.y);
// Write new position out to a texture for processing in the visual effects shader
gl_FragColor = vec4(newPosition.x, newPosition.y, velocity.x, velocity.y);
} else { // this point will not be rendered on screen
// Write new position out off screen (all -1 cellIndexes have off-screen offset values)
gl_FragColor = vec4( offsets.x, offsets.y, 0.0, 0.0);
}
From the physics shader the tPositions texture with the points' new movements is rendered out and passed to the visual effects shaders:
Visual Effects Shader (vert):
uniform sampler2D tPositions; // passed in from the Physics Shader
uniform sampler2D tSelectionFactors;
uniform float uPerMotifBufferDimension;
uniform sampler2D uTextureSheet;
uniform float uPointSize;
uniform float uTextureCoordSizeX;
uniform float uTextureCoordSizeY;
attribute float aTextureIndex;
attribute float aAlpha;
attribute float aCellIndex;
varying float vCellIndex;
varying vec2 vTextureCoords;
varying vec2 vTextureSize;
varying float vAlpha;
varying vec3 vColor;
...omitted noise functions for brevity...
void main() {
vec4 tmpPos = texture2D( tPositions, position.xy );
vec2 pos = tmpPos.xy;
vec2 vel = tmpPos.zw;
vCellIndex = aCellIndex;
if (vCellIndex >= 0.0) { // this point will be rendered onscreen
float texelSize = 1.0 / uPerMotifBufferDimension;
vec2 perMotifUV = vec2( mod(aCellIndex, uPerMotifBufferDimension)*texelSize, floor(aCellIndex / uPerMotifBufferDimension)*texelSize );
perMotifUV += vec2(0.5*texelSize);
vec4 selectionFactors = texture2D( tSelectionFactors, perMotifUV ).xyzw;
float aSelectedMotif = selectionFactors.x;
float aColor = selectionFactors.y;
float fadeFactor = selectionFactors.z;
vTextureCoords = vec2( aTextureIndex * uTextureCoordSizeX, 0 );
vTextureSize = vec2( uTextureCoordSizeX, uTextureCoordSizeY );
vAlpha = aAlpha * fadeFactor;
vColor = vec3( 1.0, aColor, 1.0 );
gl_PointSize = uPointSize;
} else { // this point will not be rendered onscreen
vAlpha = 0.0;
vColor = vec3(0.0, 0.0, 0.0);
gl_PointSize = 0.0;
}
gl_Position = projectionMatrix * modelViewMatrix * vec4( pos.x, pos.y, position.z, 1.0 );
}
Visual Effects Shader (frag):
uniform sampler2D tPositions;
uniform sampler2D uTextureSheet;
varying float vCellIndex;
varying vec2 vTextureCoords;
varying vec2 vTextureSize;
varying float vAlpha;
varying vec3 vColor;
void main() {
gl_FragColor = vec4( vColor, vAlpha );
if (vCellIndex >= 0.0) { // this point will be rendered onscreen, so add the texture
vec2 realTexCoord = vTextureCoords + ( gl_PointCoord * vTextureSize );
gl_FragColor = gl_FragColor * texture2D( uTextureSheet, realTexCoord );
}
}
Thanks to #Blindman67's comment above, I sorted out the problem. It had nothing to do with the shaders. In the Javascript (Three.js) I needed to signal the GPU to delete old textures before adding the updated ones.
Everytime I update a texture (most of mine are DataTextures) I need to call dispose() on the existing texture before creating and updating the new one, like so:
var textureHandle; // holds a reference to the current texture uniform value
textureHandle.dispose(); // ** deallocates GPU memory **
textureHandle = new THREE.DataTexture( textureData, dimension, dimension, THREE.RGBAFormat, THREE.FloatType );
textureHandle.needsUpdate = true;
uniforms.textureHandle.value = textureHandle;

Resources