#javascript #html #three.js #webgl
#javascript #HTML #three.js #webgl
Вопрос:
Вот пример, основанный на втором живом примере из https://threejsfundamentals.org/threejs/lessons/threejs-shadertoy.html:
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
// Three.js - Shadertoy Basic
// from https://threejsfundamentals.org/threejs/threejs-shadertoy-basic.html
import * as THREE from 'https://unpkg.com/three@0.122.0/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
renderer.autoClearColor = false;
const camera = new THREE.PerspectiveCamera(
45, 16/9, 0.01, 1000
);
camera.position.z = 5
const scene = new THREE.Scene();
const plane = new THREE.PlaneBufferGeometry(2, 2);
const fragmentShader = `
#include <common>
uniform vec3 iResolution;
uniform float iTime;
// https://www.shadertoy.com/view/MtXSWj
float alternate(float p, float d){;
return sign(fract(p*d*.5)*2.-1.);
}
vec3 rainbow(float t){
return sin(t vec3(0,.33,.66)*6.28)*.5 .5;
}
vec3 TwinDragon(vec2 p){
float time = fract(iTime*0.05)*20.;
//scaling
p = (p*2.-iResolution.xy)/iResolution.y*1.5;
//----------the fractal stuff---- ---THIS IS ANIMATIONS----(so remove them if you want)
p.y = alternate(p.x, 256. )/512. * clamp(time-16.,0.,2.)/2.;
p.x -= alternate(p.y, 128. )/256. * clamp(time-14.,0.,2.)/2.;
p.y = alternate(p.x, 64. )/128. * clamp(time-12.,0.,2.)/2.;
p.x -= alternate(p.y, 32. )/ 64. * clamp(time-10.,0.,2.)/2.;
p.y = alternate(p.x, 16. )/ 32. * clamp(time- 8.,0.,2.)/2.;
p.x -= alternate(p.y, 8. )/ 16. * clamp(time- 6.,0.,2.)/2.;
p.y = alternate(p.x, 4. )/ 8. * clamp(time- 4.,0.,2.)/2.;
p.x -= alternate(p.y, 2. )/ 4. * clamp(time- 2.,0.,2.)/2.;
// prettifying
vec2 block = ceil(p .5); //index for blocks from which the fractal is shifted
vec3 color = rainbow(block.x*4. block.y); //rainbow palette using block index as t
float dis = length(fract(p .5)*2.-1.);//distance to middle of block
color *= .5 dis*.7; //using distance within block for some more pretty.
return color;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord ){
vec2 d = vec2(.5,0);
//some antialiasing
vec3 col = (
TwinDragon(fragCoord d.xy)
TwinDragon(fragCoord-d.xy)
TwinDragon(fragCoord d.yx)
TwinDragon(fragCoord-d.yx)
)*.25;
fragColor = vec4(col,1.);
}
void main() {
mainImage(gl_FragColor, gl_FragCoord.xy);
}
`;
const uniforms = {
iTime: { value: 0 },
iResolution: { value: new THREE.Vector3() },
};
const material = new THREE.ShaderMaterial({
fragmentShader,
uniforms,
side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(plane, material)
scene.add(mesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001; // convert to seconds
resizeRendererToDisplaySize(renderer);
const canvas = renderer.domElement;
uniforms.iResolution.value.set(canvas.width, canvas.height, 1);
uniforms.iTime.value = time;
mesh.rotation.y = 0.01
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
Но, как вы видите, когда я переключаюсь на PerspectiveCamera и поворачиваю плоскость, текстура не преобразуется вместе с объектом.
Какой самый простой способ изменить пример, чтобы преобразовать шейдер вместе с плоскостью, чтобы изображение отображалось на поверхности плоскости, а не на плоскости, похожей на маску?
Комментарии:
1. Разве этот пример не находится дальше на странице, на которую вы ссылаетесь? Он показывает сопоставление шейдера shadertoy с кубом с использованием uv-координат
2. @gman Действительно! Но мне потребовалось некоторое время, чтобы понять, в чем разница. Полный новичок.
Ответ №1:
Ответ заключается в передаче uv-координат из вершинного шейдера в фрагментный шейдер с использованием varying
переменной.
В принципе, мы можем заменить строки
const fragment = `
... clipped ...
void main() {
mainImage(gl_FragColor, gl_FragCoord.xy);
}
`;
const uniforms = {
iTime: { value: 0 },
iResolution: { value: new THREE.Vector3() },
};
const material = new THREE.ShaderMaterial({
fragmentShader,
с помощью
const fragment = `
... clipped ...
varying vec2 vUv;
void main() {
mainImage(gl_FragColor, vUv * iResolution.xy);
}
`;
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
const uniforms = {
iTime: { value: 0 },
iResolution: { value: new THREE.Vector3() },
};
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
и мы получаем следующий результат:
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
// Three.js - Shadertoy Basic
// from https://threejsfundamentals.org/threejs/threejs-shadertoy-basic.html
import * as THREE from 'https://unpkg.com/three@0.122.0/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
renderer.autoClearColor = false;
const camera = new THREE.PerspectiveCamera(
45, 16/9, 0.01, 1000
);
camera.position.z = 5
const scene = new THREE.Scene();
const plane = new THREE.PlaneBufferGeometry(2, 2);
const fragmentShader = `
#include <common>
uniform vec3 iResolution;
uniform float iTime;
// https://www.shadertoy.com/view/MtXSWj
float alternate(float p, float d){;
return sign(fract(p*d*.5)*2.-1.);
}
vec3 rainbow(float t){
return sin(t vec3(0,.33,.66)*6.28)*.5 .5;
}
vec3 TwinDragon(vec2 p){
float time = fract(iTime*0.05)*20.;
//scaling
p = (p*2.-iResolution.xy)/iResolution.y*1.5;
//----------the fractal stuff---- ---THIS IS ANIMATIONS----(so remove them if you want)
p.y = alternate(p.x, 256. )/512. * clamp(time-16.,0.,2.)/2.;
p.x -= alternate(p.y, 128. )/256. * clamp(time-14.,0.,2.)/2.;
p.y = alternate(p.x, 64. )/128. * clamp(time-12.,0.,2.)/2.;
p.x -= alternate(p.y, 32. )/ 64. * clamp(time-10.,0.,2.)/2.;
p.y = alternate(p.x, 16. )/ 32. * clamp(time- 8.,0.,2.)/2.;
p.x -= alternate(p.y, 8. )/ 16. * clamp(time- 6.,0.,2.)/2.;
p.y = alternate(p.x, 4. )/ 8. * clamp(time- 4.,0.,2.)/2.;
p.x -= alternate(p.y, 2. )/ 4. * clamp(time- 2.,0.,2.)/2.;
// prettifying
vec2 block = ceil(p .5); //index for blocks from which the fractal is shifted
vec3 color = rainbow(block.x*4. block.y); //rainbow palette using block index as t
float dis = length(fract(p .5)*2.-1.);//distance to middle of block
color *= .5 dis*.7; //using distance within block for some more pretty.
return color;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord ){
vec2 d = vec2(.5,0);
//some antialiasing
vec3 col = (
TwinDragon(fragCoord d.xy)
TwinDragon(fragCoord-d.xy)
TwinDragon(fragCoord d.yx)
TwinDragon(fragCoord-d.yx)
)*.25;
fragColor = vec4(col,1.);
}
varying vec2 vUv;
void main() {
mainImage(gl_FragColor, vUv * iResolution.xy);
}
`;
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
const uniforms = {
iTime: { value: 0 },
iResolution: { value: new THREE.Vector3() },
};
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms,
side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(plane, material)
scene.add(mesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001; // convert to seconds
resizeRendererToDisplaySize(renderer);
const canvas = renderer.domElement;
uniforms.iResolution.value.set(canvas.width, canvas.height, 1);
uniforms.iTime.value = time;
mesh.rotation.y = 0.01
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>