I have had no problems loading others' shaders into THREE.js, but I seem to have run into a problem creating and running my own.
The shader works on the site I used to create it (https://shaderfrog.com/app/view/5460), but not when I tried to implement it.
I am not sure whether it is a javascript, three.js or GLSL problem, so all help would be greatly appreciated.
Here is my js code:
function newCube(colour=0xffffff,height=1,width=height,depth=height){
const geometry = new THREE.CubeGeometry(height,depth,width);
const material = new THREE.MeshBasicMaterial( {color: colour} );
return(new THREE.Mesh( geometry, material ));
}
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,1,10000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xaaaaaa, 1);
document.body.appendChild(renderer.domElement);
const cube = newCube(0x220033,1);
var material= new THREE.RawShaderMaterial({
"id": 5472,
"name": "Fragmented",
"fragmentShader": "precision highp float;\nprecision highp int;\n\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nuniform float time;\nuniform vec3 color;\nuniform vec3 lightPosition;\nvarying vec3 vPosition;\nvarying vec3 vNormal;\nvarying vec3 vColor;\nvarying vec2 vUv;\nvarying vec2 vUv2;\n\nvoid main() {\n vec3 worldPosition = ( modelMatrix * vec4( vPosition, 1.0 )).xyz;\n vec3 worldNormal = normalize( vec3( modelMatrix * vec4( vNormal, 0.0 ) ) );\n vec3 lightVector = normalize( lightPosition - worldPosition );\n float brightness = dot( worldNormal, lightVector );\n gl_FragColor = vec4( color * brightness, 1.0 );\n}",
"vertexShader": "precision highp float;\nprecision highp int;\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nuniform float time;\nuniform vec3 color;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\nvarying vec3 vPosition;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec2 vUv2;\nvoid main() {\n vNormal = normal;\n vUv = uv;\n vUv2 = uv2;\n vPosition = position;\n gl_Position= projectionMatrix * modelViewMatrix * vec4( position+sin(position*time)/10., 1. );\n}",
"uniforms": {
"time": {
"name": "time",
"displayName": null,
"type": "f",
"glslType": "float",
"useGridHelper": false,
"useRange": false,
"range": null,
"isRandom": false,
"randomRange": null,
"useToggle": false,
"toggle": null,
"description": "",
"value":0
},
"color": {
"name": "color",
"displayName": null,
"type": "c",
"glslType": "vec3",
"useGridHelper": false,
"useRange": false,
"range": null,
"isRandom": false,
"randomRange": null,
"useToggle": false,
"toggle": null,
"description": "Example light color"
},
"lightPosition": {
"name": "lightPosition",
"displayName": null,
"type": "v3",
"glslType": "vec3",
"useGridHelper": false,
"useRange": false,
"range": null,
"isRandom": false,
"randomRange": null,
"useToggle": false,
"toggle": null,
"description": ""
}
}
});
scene.add(cube);
cube.material=material;
camera.position.z = 3;
function render() {
cube.material.uniforms.time.value+=0.1;
renderer.render(scene, camera);
cube.mesh.rotation.x += 0.05;
cube.mesh.rotation.y -= 0.05;
requestAnimationFrame(render);
}
render();
I modified your code, so at least it shows something:
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.133";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,1,10000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xaaaaaa, 1);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.BoxGeometry();
var material= new THREE.RawShaderMaterial({
"fragmentShader": "precision highp float;\nprecision highp int;\n\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nuniform float time;\nuniform vec3 color;\nuniform vec3 lightPosition;\nvarying vec3 vPosition;\nvarying vec3 vNormal;\nvarying vec3 vColor;\nvarying vec2 vUv;\nvarying vec2 vUv2;\n\nvoid main() {\n vec3 worldPosition = ( modelMatrix * vec4( vPosition, 1.0 )).xyz;\n vec3 worldNormal = normalize( vec3( modelMatrix * vec4( vNormal, 0.0 ) ) );\n vec3 lightVector = normalize( lightPosition - worldPosition );\n float brightness = dot( worldNormal, lightVector );\n gl_FragColor = vec4( color * brightness, 1.0 );\n}",
"vertexShader": "precision highp float;\nprecision highp int;\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nuniform float time;\nuniform vec3 color;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\nvarying vec3 vPosition;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec2 vUv2;\nvoid main() {\n vNormal = normal;\n vUv = uv;\n vUv2 = uv2;\n vPosition = position;\n gl_Position= projectionMatrix * modelViewMatrix * vec4( position+sin(position*time)/10., 1. );\n}",
"uniforms": {
"time": {
"value":0
},
"color": {
"value": new THREE.Color(0x320064)
},
"lightPosition": {
"value": new THREE.Vector3().setScalar(5)
}
}
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 3;
function render() {
cube.material.uniforms.time.value+=0.1;
renderer.render(scene, camera);
cube.rotation.x += 0.01;
cube.rotation.y -= 0.01;
requestAnimationFrame(render);
}
render();
</script>
Related
I am trying to recreate the Volumetric Lighting example from JMSWRNR ( https://codesandbox.io/s/github/jmswrnr/website-examples/tree/master/3d-header/volumetric-lighting?from-embed=&file=/src/index.js ) but I am facing some issues with Occlusion Composer.
I don't really know GLSL so debugging is very hard especially for a beginner like me.
Here is the GitHub Repo: https://github.com/RolandTeslaru/roland-teslaru-portfolio/tree/not-working
Main Code is in the components/HomeCanvas/Scene.ts
My guess its that the VolumetricLightCylinder.js shader is causing the issue because when I change
worldPosition.xyz;
to
worldPosition
the error disappears.
import * as THREE from 'three'
export default () => ({
lights: true,
transparent: true,
depthWrite: false,
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib['lights'],
{
attenuation: { value: 25.0 },
anglePower: { value: 10.0 },
spotPosition: { value: new THREE.Vector3(0, 0, 0) },
},
]),
vertexShader: /* glsl */ `
#include <common>
struct PointLight {
vec3 position;
vec3 color;
float distance;
float decay;
int shadow;
float shadowBias;
float shadowRadius;
vec2 shadowMapSize;
float shadowCameraNear;
float shadowCameraFar;
};
uniform PointLight pointLights[NUM_POINT_LIGHTS];
uniform float viewMix;
varying vec4 vColor;
varying vec3 vNormal;
varying vec3 vWorldPosition;
float _punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
if( decayExponent > 0.0 ) {
return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );
}
return 1.0;
}
void main() {
vNormal = normalize(normalMatrix * normal);
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewPosition;
vec4 addedLights = vec4(0.1, 0.1, 0.1, 1.0);
for(int l = 0; l < NUM_POINT_LIGHTS; l++) {
float dist = distance(pointLights[l].position, worldPosition.xyz);
addedLights.rgb += pointLights[l].color *
_punctualLightIntensityToIrradianceFactor(
dist,
pointLights[l].distance,
pointLights[l].decay
);
}
vWorldPosition = worldPosition.xyz;
vColor = addedLights;
}
`,
fragmentShader: /* glsl */`
varying vec3 vNormal;
varying vec3 vWorldPosition;
uniform vec3 spotPosition;
uniform float attenuation;
uniform float anglePower;
varying vec4 vColor;
void main() {
float intensity;
intensity = distance(vWorldPosition, spotPosition)/attenuation;
intensity = 1.0 - clamp(intensity, 0.0, 1.0);
vec3 normal = vec3(vNormal.x, vNormal.y, abs(vNormal.z));
float angleIntensity = pow(dot(normal, vec3(0.0, 0.0, 1.0)), anglePower);
intensity = intensity * angleIntensity;
gl_FragColor = vec4(vColor.rgb, intensity);
}
`,
})
I am trying to use three.js blending and I have been trying to get MultiplyBlending, SubtractiveBlending, NoBlending to work but all that I get are white squares:
When I use AdditiveBlending I get something that works:
fragment shader:
uniform sampler2D diffuseTexture;
varying vec4 vColor;
varying vec2 vAngle;
void main() {
vec2 coords = (gl_PointCoord - 0.5) * mat2(vAngle.x, vAngle.y, -vAngle.y, vAngle.x) + 0.5;
gl_FragColor = texture2D(diffuseTexture, coords) * vColor;
}
vertex shader:
uniform float pointMultiplier;
attribute float size;
attribute float angle;
varying vec4 vColor;
varying vec2 vAngle;
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * mvPosition;
gl_PointSize = size * pointMultiplier / gl_Position.w;
vAngle = vec2(cos(angle), sin(angle));
vColor = color;
}
This is how I am creating the material and geometry in the TypeScript:
this.material = new Three.ShaderMaterial({
uniforms: {
diffuseTexture: {
value: new Three.TextureLoader().load(basicParticle)
},
pointMultiplier: {
value: window.innerHeight / (2.0 * Math.tan(0.5 * 60.0 * Math.PI / 180.0))
}
},
vertexShader,
fragmentShader,
blending: Three.MultiplyBlending,
depthTest: true,
depthWrite: false,
transparent: true,
vertexColors: true
});
this.geometry = new Three.BufferGeometry();
new Three.Points(this.geometry, this.material);
What is causing me to get white squares and how can I fix it? Is it the shaders, TypeScript, or both?
How do I set the color to the mesh only when the height is zero?
As for now, i just mixed the colors:
The problem is that this kind on mixing is not precise. I just want the color blue only when the height is zero (so only inside that red path I made with paint).
I created a custom material for the mesh, like so:
material = new THREE.ShaderMaterial({
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib['lights'],
{
lightIntensity: {type: 'f', value: 1.0},
diffuse: {type: 'c', value: new THREE.Color(0x0000ff)},
color0: {
value: new THREE.Color("blue")
},
color1: {
value: new THREE.Color("green")
},
color2: {
value: new THREE.Color("brown")
},
color3: {
value: new THREE.Color("black")
},
bboxMin: {
value: geom.boundingBox.min
},
bboxMax: {
value: geom.boundingBox.max
}
}
]),
vertexShader: `
uniform vec3 bboxMin;
uniform vec3 bboxMax;
varying vec2 vUv;
varying vec3 vPos;
varying vec3 vNormal;
void main() {
vPos = (modelMatrix * vec4(position, 1.0 )).xyz;
vNormal = normalMatrix * normal;
vUv.y = (position.y - bboxMin.y) / (bboxMax.y - bboxMin.y);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`,
fragmentShader: `
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
uniform vec3 color0;
varying vec2 vUv;
uniform vec3 diffuse;
varying vec3 vPos;
varying vec3 vNormal;
struct PointLight {
vec3 position;
vec3 color;
};
uniform PointLight pointLights[ NUM_POINT_LIGHTS ];
void main() {
vec4 addedLights = vec4(0.1, 0.1, 0.1, 1.0);
for(int l = 0; l < NUM_POINT_LIGHTS; l++) {
vec3 adjustedLight = pointLights[l].position + cameraPosition;
vec3 lightDirection = normalize(vPos - adjustedLight);
addedLights.rgb += clamp(dot(-lightDirection, vNormal), 0.0, 1.0) * pointLights[l].color;
}
gl_FragColor = mix(vec4(mix(mix(mix(color0, color1, vUv.y), color1, vUv.y), mix(color1, color2, vUv.y), vUv.y), 1.0),addedLights, addedLights);
}
`,
lights: true
});
Try using the step() function. Here's a definition to help you understand it. Here's how it works:
float step(float edge, float x)
It takes in a constant to declare the edge, and x, which is your variable.
If x is below the edge, you get 0, and if x is above the edge, you get 1.
Here's a simplified use of it. When height is below 0.2, you'll get blue, and when height is above 0.2, you'll get green.
vec3 green = vec3(0.0, 1.0, 0.0);
vec3 blue = vec3(0.0, 0.0, 1.0);
float edge = 0.2;
float colorMix = step(edge, height);
vec3 finalColor = mix(blue, green, colorMix);
I picked 0.2 to give the blue band some thickness, otherwise it wouldn't be visible.
I would like to import that shader on my project:
https://shaderfrog.com/app/view/2447
This is my fragment shader:
<script id="fragmentShader" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
uniform sampler2D texture1;
// Example varyings passed from the vertex shader
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
vec3 color = vec3(texture2D(texture1, vUv));
vec3 outcolor=color;
float v = u_time + (vUv.x*0.5 + vUv.y*0.5);
vec2 Uv2 = vec2(color.r+color.b+v,color.g+color.b+v);
outcolor = vec3(texture2D(texture1, Uv2));
gl_FragColor = vec4( outcolor, 1.0 );
}
</script>
And this is my vertex shader:
<script id="vertexShader" type="x-shader/x-vertex">
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
vNormal = normal;
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
My shader is compiling, but I don't have the same result, I only have a highly frozen contrasted image. Plus, I do increment my uniform u_time value on a requestAnimationFrame function.
I can't see what I'm doing wrong?
I've simplified your code and made it work with the latest revision (r96).
Pay attention to the settings of the texture.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 2);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var tex = new THREE.TextureLoader().load("https://threejs.org/examples/textures/UV_Grid_Sm.jpg");
tex.wrapS = THREE.RepeatWrapping;
tex.wrapT = THREE.RepeatWrapping;
var geo = new THREE.PlaneBufferGeometry(2, 2);
var mat = new THREE.ShaderMaterial({
uniforms:{
time: {value: 0},
texture1: {value: tex}
},
vertexShader:`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`,
fragmentShader: `
uniform float time;
uniform sampler2D texture1;
varying vec2 vUv;
void main() {
vec3 c = vec3(texture2D(texture1, vUv));
float v = time + (vUv.x*0.5 + vUv.y*0.5);
vec2 Uv2 = vec2(c.r + c.b+v, c.g + c.b + v);
vec3 outcolor = vec3(texture2D(texture1, Uv2));
gl_FragColor = vec4( outcolor, 1.0 );
}
`
});
var plane = new THREE.Mesh(geo, mat);
scene.add(plane);
var clock = new THREE.Clock();
var time = 0;
render();
function render(){
requestAnimationFrame(render);
time += clock.getDelta();
mat.uniforms.time.value = time;
renderer.render(scene, camera);
}
body{
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
I have this material:
<script id="vertexShader2" type="x-shader/x-vertex">
varying vec2 vUv;
void main()
{
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<!-- fragment shader a.k.a. pixel shader -->
<script id="fragmentShader2" type="x-shader/x-vertex">
uniform sampler2D baseTexture;
uniform float baseSpeed;
uniform sampler2D noiseTexture;
uniform float noiseScale;
uniform float alpha;
uniform float time;
varying vec2 vUv;
void main()
{
vec2 uvTimeShift = vUv + vec2( -0.7, 1.5 ) * time * baseSpeed;
vec4 noiseGeneratorTimeShift = texture2D( noiseTexture, uvTimeShift );
vec2 uvNoiseTimeShift = vUv + noiseScale * vec2( noiseGeneratorTimeShift.r, noiseGeneratorTimeShift.b );
vec4 baseColor = texture2D( baseTexture, uvNoiseTimeShift );
baseColor.a = alpha;
gl_FragColor = baseColor;
}
</script>
var noiseTexture = new THREE.ImageUtils.loadTexture( 'img/wormholes/cloud.png' );
noiseTexture.wrapS = noiseTexture.wrapT = THREE.RepeatWrapping;
var lavaTexture = new THREE.ImageUtils.loadTexture( "img/planets/"+value['texture_clouds'] );
lavaTexture.wrapS = lavaTexture.wrapT = THREE.RepeatWrapping;
this.earthUniforms = {
baseTexture: { type: "t", value: lavaTexture },
baseSpeed: { type: "f", value: 0.001 },
noiseTexture: { type: "t", value: noiseTexture },
noiseScale: { type: "f", value: 0.07337 },
alpha: { type: "f", value: 10.0 },
time: { type: "f", value: 1.0 }
};
animatedSurfaces.push(earthUniforms);
var customMaterial = new THREE.ShaderMaterial(
{
uniforms: earthUniforms,
vertexShader: document.getElementById( 'vertexShader2' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader2' ).textContent,
specular : new THREE.Color("rgb(255,255,255)"),
shininess : 0.1,
depthTest : 0,
blending : 1,
transparent: true,
bumpScale : 1, //0.8
bumpMap : THREE.ImageUtils.loadTexture( "img/planets/"+value['texture_clouds_alpha'] ),
map : THREE.ImageUtils.loadTexture( "img/planets/"+value['texture_clouds'] ),
color : 0xffffff,
//opacity : 0.1,
emissive : new THREE.Color("rgb(30,22,20)"),
alphaMap : THREE.ImageUtils.loadTexture( "img/planets/"+value['texture_clouds_alpha']),
});
It is reacting with light like MeshBasic material.
I need to react like MeshLambert material with alpha map.
It means to configure material black color in "lavatexture" to be transparent, and reacting on light source. Lighted side to have diffuse color, and side in shadow to have specular color.
How to solve this, when uniforms don't let material to behive like meshLambertMaterial, i cant set diffuse and specular color.
But i need that uniforms effect. (lava effect http://stemkoski.github.io/Three.js/Shader-Animate.html)
r71