According to the layout, the shader should be divided into 6 columns and 3 rows with dimensions of 100vw up by 100vh. The sizes of the rows should be divided evenly proportionally by height. The problem is that the width of the shader segments is different and should be in proportions of 17.65vw, 16.25vw, 16.32vw, 16.32vw, 16.25vw, 17.15vw from left to right. At the moment they are the same width and I can not achieve the desired result.
I had never worked with three.js before and the initial code was given me by web-designer(сode),which was on React and I need it on JS, so I rewrote it, but it was only squared segments of shader in it, which cannot be turned into other shapes and sizes. I did some reading on three.js and went through given code, but couldn’t succeed. Here is my code:
const canvas = document.getElementById('canvas');
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(
window.innerWidth / -2, window.innerWidth / 2,
window.innerHeight / 2, window.innerHeight / -2,
0.1, 1000
);
camera.position.z = 1;
window.addEventListener('resize', () => {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width, height);
camera.left = width / -2;
camera.right = width / 2;
camera.top = height / 2;
camera.bottom = height / -2;
camera.updateProjectionMatrix();
});
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectionPosition = projectionMatrix * viewPosition;
gl_Position = projectionPosition;
}
`;
const fragmentShader = `
uniform float time;
uniform vec2 resolution;
uniform vec2 pointer;
varying vec2 vUv;
vec3 palette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.263, 0.416, 0.557);
return a + b * cos(6.28318 * (c * t + d));
}
void main() {
vec2 uv = (gl_FragCoord.xy * 2.0 - resolution.xy) / resolution.y;
vec2 uv0 = uv;
vec3 finalColor = vec3(0.0);
vec2 gridSize = vec2(6.0, 3.0);
vec2 gridPos = floor(vUv * gridSize);
vec2 localUv = fract(vUv * gridSize) - 0.5;
uv = sin(localUv * 0.5) - pointer;
float d = length(uv) * exp(-length(uv0));
vec3 col = palette(length(uv0) + time * 0.4);
d = sin(d * 8.0 + time) / 8.0;
d = abs(d);
d = pow(0.02 / d, 2.0);
finalColor += col * d;
gl_FragColor = vec4(finalColor, 1.0);
}
`;
const uniforms = {
time: { value: 0 },
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
pointer: { value: new THREE.Vector2(0, 0) }
};
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms
});
const geometry = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight);
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
const clock = new THREE.Clock();
function animate() {
uniforms.time.value += clock.getDelta();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
#canvas {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
<canvas id="canvas"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
Can you please help me solve this problem and make shader’s column (segments’) width adjustable and different from each other
Andrew Kargin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
you can add more segments to PlaneGeometry
const widthSegments = 6
const heightSegments = 3
const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
and then modify geometry.attributes.position
and as a result you will change behaviour like like this:
so here is function which will do that:
function modifyPlaneGeomPosAttr(planeGeometry, widthSegments, heightSegments) {
function check(value, valueCheck) {
if ( !(value instanceof Array) || (value instanceof Array && value?.length != valueCheck)) {
if (value instanceof Number) {
return new Array(value).fill(1)
} else {
return new Array(valueCheck).fill(1)
}
} else {
return value
}
}
widthSegments = check(widthSegments , planeGeometry.parameters.widthSegments )
heightSegments = check(heightSegments, planeGeometry.parameters.heightSegments)
const widthPos = widthSegments .reduce((ac,a,i)=>{ac[i+1]=ac[i]+a; return ac}, new Array(widthSegments .length+1).fill(0)).map((a,_,A)=>a/A.at(-1))
const heightPos = heightSegments.reduce((ac,a,i)=>{ac[i+1]=ac[i]+a; return ac}, new Array(heightSegments.length+1).fill(0)).map((a,_,A)=>a/A.at(-1))
const pos = planeGeometry.attributes.position.array
let i = 0;
for (yPos of heightPos) {
y = (yPos-0.5)*planeGeometry.parameters.height
for (xPos of widthPos) {
x = (xPos-0.5)*planeGeometry.parameters.width
pos[i++] = x
pos[i++] = -y
pos[i++] = 0
}
}
}
planeGeometry
is THREE.PlaneGeometry you want to change
widthSegments
must be Array with length equal to widthSegments of PlaneGeometry (if dont want to change widthSegments just set to null
)
heightSegments
must be Array with length equal to heightSegments of PlaneGeometry (if dont want to change heightSegments just set to null
or don`t write value for it at all)
and here is example how to use it:
const geometry = new THREE.PlaneGeometry(width, height, 6, 3);
modifyPlaneGeomPosAttr(geometry, [1,2,3,4,5,6], [1,2,3])
in this example:
widthSegments
= [1,2,3,4,5,6]
and this is relative width for each segment (width of plane will be the same)
heightSegments
= [1,2,3]
and this is relative height for each segment (height of plane will be the same)
and here is result:
so for your case set widthSegments
= [17.65, 16.25, 16.32, 16.32, 16.25, 17.15]
full code:
function modifyPlaneGeomPosAttr(planeGeometry, widthSegments, heightSegments) {
function check(value, valueCheck) {
if ( !(value instanceof Array) || (value instanceof Array && value?.length != valueCheck)) {
if (value instanceof Number) {
return new Array(value).fill(1)
} else {
return new Array(valueCheck).fill(1)
}
} else {
return value
}
}
widthSegments = check(widthSegments , planeGeometry.parameters.widthSegments )
heightSegments = check(heightSegments, planeGeometry.parameters.heightSegments)
const widthPos = widthSegments .reduce((ac,a,i)=>{ac[i+1]=ac[i]+a; return ac}, new Array(widthSegments .length+1).fill(0)).map((a,_,A)=>a/A.at(-1))
const heightPos = heightSegments.reduce((ac,a,i)=>{ac[i+1]=ac[i]+a; return ac}, new Array(heightSegments.length+1).fill(0)).map((a,_,A)=>a/A.at(-1))
const pos = planeGeometry.attributes.position.array
let i = 0;
for (yPos of heightPos) {
y = (yPos-0.5)*planeGeometry.parameters.height
for (xPos of widthPos) {
x = (xPos-0.5)*planeGeometry.parameters.width
pos[i++] = x
pos[i++] = -y
pos[i++] = 0
}
}
}
const canvas = document.getElementById('canvas');
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(
window.innerWidth / -2, window.innerWidth / 2,
window.innerHeight / 2, window.innerHeight / -2,
0.1, 1000
);
camera.position.z = 1;
window.addEventListener('resize', () => {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width, height);
camera.left = width / -2;
camera.right = width / 2;
camera.top = height / 2;
camera.bottom = height / -2;
camera.updateProjectionMatrix();
});
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectionPosition = projectionMatrix * viewPosition;
gl_Position = projectionPosition;
}
`;
const fragmentShader = `
uniform float time;
uniform vec2 resolution;
uniform vec2 pointer;
varying vec2 vUv;
vec3 palette(float t) {
vec3 a = vec3(0.5, 0.5, 0.5);
vec3 b = vec3(0.5, 0.5, 0.5);
vec3 c = vec3(1.0, 1.0, 1.0);
vec3 d = vec3(0.263, 0.416, 0.557);
return a + b * cos(6.28318 * (c * t + d));
}
void main() {
vec2 uv = (gl_FragCoord.xy * 2.0 - resolution.xy) / resolution.y;
vec2 uv0 = uv;
vec3 finalColor = vec3(0.0);
vec2 gridSize = vec2(6.0, 3.0);
vec2 gridPos = floor(vUv * gridSize);
vec2 localUv = fract(vUv * gridSize) - 0.5;
uv = sin(localUv * 0.5) - pointer;
float d = length(uv) * exp(-length(uv0));
vec3 col = palette(length(uv0) + time * 0.4);
d = sin(d * 8.0 + time) / 8.0;
d = abs(d);
d = pow(0.02 / d, 2.0);
finalColor += col * d;
gl_FragColor = vec4(finalColor, 1.0);
}
`;
const uniforms = {
time: { value: 0 },
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
pointer: { value: new THREE.Vector2(0, 0) }
};
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms
});
const geometry = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight, 6);
modifyPlaneGeomPosAttr(geometry, [17.65, 16.25, 16.32, 16.32, 16.25, 17.15])
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
const clock = new THREE.Clock();
function animate() {
// uniforms.time.value += clock.getDelta();
uniforms.time.value = parseFloat(document.querySelector('input[type=range]').value)*2*Math.PI
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
#canvas {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
body {
margin: 0;
padding: 0;
}
input[type=range] {
position: absolute;
width: calc(100vw - 10px);
margin: 5px;
padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<canvas id="canvas"></canvas>
<input type="range" min=0 max=1 step=0.001 value=0.385>