根据布局,着色器应分为 6 列和 3 行,尺寸为 100vw 到 100vh。行的大小应按高度按比例均匀划分。问题是着色器段的宽度不同,从左到右的比例应该是17.65vw、16.25vw、16.32vw、16.32vw、16.25vw、17.15vw。目前它们的宽度相同,我无法达到预期的结果。
我以前从未使用过 Three.js,最初的代码是由网页设计师(сode)给我的,它是在 React 上的,我需要在 JS 上使用它,所以我重写了它,但它只是其中的着色器,无法转变成其他形状和尺寸。我在 Three.js 上阅读了一些内容并浏览了给定的代码,但没有成功。这是我的代码:
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>
你能帮我解决这个问题并使着色器的列(段)宽度可调且彼此不同吗
您可以向 PlaneGeometry 添加更多线段
const widthSegments = 6
const heightSegments = 3
const geometry = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
然后修改
geometry.attributes.position
,结果你将改变 uv 行为,如:
所以这里有一个函数可以做到这一点:
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) {0
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
是 THREE.PlaneGeometry 你想要改变widthSegments
必须是长度等于 PlaneGeometry widthSegments 的数组(如果不想更改 widthSegments 只需设置为 null
)heightSegments
必须是长度等于 PlaneGeometry heightSegments 的数组(如果不想更改 heightSegments,只需将其设置为 null
或根本不为其写入值)
这是如何使用它的示例:
const geometry = new THREE.PlaneGeometry(width, height, 6, 3);
modifyPlaneGeomPosAttr(geometry, [1,2,3,4,5,6], [1,2,3])
在此示例中:
widthSegments
= [1,2,3,4,5,6]
这是每个线段的相对宽度(平面宽度相同)heightSegments
= [1,2,3]
这是每个段的相对高度(平面高度相同)widthSegments
= [17.65, 16.25, 16.32, 16.32, 16.25, 17.15]
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) {0
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>