shaders in three.js. terug naar de inleiding
ga naar de animatie in fullscreen
Schets met twee fragmentShaders
fragmentShader1
(regel 28 t/m 35) is het ShaderMaterial van de box (regel 109 t/m 113).
De mix functie (regel 43) geeft een groen (colorB, regel 54) rode (colorA, regel 53) mix kleur op de box
De mixfactor is het x kanaal van de vUv vector. Het canvas is genormaliseerd tussen [-1 , +1]
daarom is de overang tussen rood en groen niet in het midden van de box.
fragmentShader2
(regel 37 t/m 45) is het ShaderMaterial van de sphere (regel 124 t/m 129)
Deze shader werkt ook met de mix functie (regel 43) met de kleur rood (colorA, regel 53) en de kleur geel (colorC, regel 55)
De mixfactor (pct in regel 42) varieert aboluut sinusvorming tussen de 0 en 1
vertexShader
(regel 19 t/m 26) De vertexShader berekend de positie van de pixel waarbinnen de fragmentShaders de pixels de kleur geven
De gl_Position wordt gebruikt in het shaderMaterial van de box (regel 112) en de sphere (regel 127)
<html>
<head>
<title>shader met 2 fragmentShaders in three.js</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8" />
<style> body { margin: 0;} </style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "../jsm/" }
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec3 vUv; //is de positie van de x,y,z coordinaten
void main() {
vUv = position;
vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewPosition;
}
</script>
<script id="fragmentShader1" type="x-shader/x-fragment">
uniform vec3 colorA;
uniform vec3 colorB;
varying vec3 vUv;
void main(){
gl_FragColor = vec4(mix(colorA, colorB, vUv.x),0.5); //0.5 is de alpha waarde van de kleur
}
</script>
<script id="fragmentShader2" type="x-shader/x-fragment">
uniform vec3 colorA;
uniform vec3 colorC;
uniform float u_time;
void main(){
float pct = abs(sin(u_time));
gl_FragColor = vec4(mix(colorA, colorC, pct), 0.8);//0.8 is de alpha waarde van de kleur
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
const uniforms = {
colorA: {type: 'vec3', value: new THREE.Color(1.00,0.00,0.00)},
colorB: {type: 'vec3', value: new THREE.Color(0.00,1.00,0.00)},
colorC: {type: 'vec3', value: new THREE.Color(1.00,1.00,0.00)},
u_time: {type: 'float',value: 1.0}
}
let scene, camera, grid, renderer , kubusM , sphereM;
//de begin waaarde van de booleans
let ruitjes = true; let sphere = false;
let wolken = true; let rotatie = true;
///data tbv de GUI
const data = {
rotatie: true, wolken: true,
sphere: true, ruitjes: false };
init();
play();
function init() {
window.addEventListener( 'resize', onWindowResize );
scene = new THREE.Scene();
createBackground();
createGrid();
createCamera();
createBox();
createSphere();
createGui(); //voor het controlvenster rechts boven
createRenderer();
createControls(); //voor de muiscontrols
onWindowResize();
}
function createBackground() {
if (wolken==false)
{const bgColor = new THREE.Color( 0xE6FBFF );
scene.background = bgColor;
wolken = true;}
else
{const loader = new THREE.TextureLoader();
const bgTexture = loader.load('../textures/cloud.jpg');
scene.background = bgTexture;
wolken = false;}
}
function createGrid() {
const size = 10;
const divisions = 10;
grid = new THREE.GridHelper( size, divisions );
grid.rotation.x = Math.PI / 2;// het ruitjespapier 90 graden draaien
scene.remove( grid );
}
function createCamera(){
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( 0, 0, 2 );
}
function createBox() {
let kubusMat = new THREE.ShaderMaterial ({
uniforms: uniforms, side: THREE.DoubleSide,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader1').textContent,
//door onderstaande code toe te voegen werkt alpha in gl_FragColor
//de material zal mengen met de achtergrond en kan transparant worden
blending: THREE.NormalBlending, transparent: true});
let kubusG = new THREE.BoxGeometry();
kubusM = new THREE.Mesh(kubusG, kubusMat);
kubusM.position.set(0,0,0);
scene.add(kubusM);
}
function createSphere() {
let sphereMat = new THREE.ShaderMaterial ({
uniforms: uniforms, wireframe: true, wireframeLinewidth: 2,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader2').textContent,
blending: THREE.NormalBlending, transparent: true});
let sphereG = new THREE.SphereGeometry(1,50,50);
sphereM = new THREE.Mesh(sphereG, sphereMat);
sphereM.position.set(0,0,0);
scene.add(sphereM);
}
function createGui() {
let gui = new GUI({width: 100});
gui.add(data, 'rotatie');
gui.add(data, 'wolken').onChange( createBackground);
gui.add(data, 'sphere').onChange( sphereOnOf);
gui.add(data, 'ruitjes').onChange( gridOnOf);
gui.open();
}
function sphereOnOf() {
if (sphere) {scene.add(sphereM), sphere = false;}
else {scene.remove(sphereM), sphere = true;}
}
function gridOnOf(){
if (ruitjes) {scene.add(grid), ruitjes = false;}
else {scene.remove(grid), ruitjes = true;}
}
function createRenderer(){
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight);
document.body.appendChild( renderer.domElement );
renderer.outputColorSpace = THREE.SRGBColorSpace;
}
function createControls() {
const controls = new OrbitControls( camera , renderer.domElement);
controls.listenToKeyEvents( window );
//als default wordt er naar de pijltjestoetsen geluister
}
function onWindowResize() {
camera.aspect = window.innerWidth/ window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function render() {
renderer.render( scene, camera );
}
function animate() {
uniforms.u_time.value += 0.02;
if (data.rotatie){
sphereM.rotation.y += 0.01; sphereM.rotation.x += 0.01;
kubusM.rotation.y -= 0.01; kubusM.rotation.x -= 0.01;}
}
function play() {
renderer.setAnimationLoop( () => {animate();render();} );
}
</script>
</body>
</html>