这行得通,但真的很乏味和无聊。顶点只是简单地从一个模型映射到另一个模型,没有任何偏移、没有重力或任何你想加入的东西。
因此,您可以应用您可以想象和编码的任何效果。这不是您问题的确切答案,但这是您可以使用着色器做什么的最简单的激励示例。剧透:一个工作示例的链接在这个答案的末尾。
让我们改变这个
进入这个
在过渡期间带有有趣的蜂拥粒子
我们将使用THREE.BoxBufferGeometry()
一些自定义属性:
var sideLenght = 10;
var sideDivision = 50;
var cubeGeom = new THREE.BoxBufferGeometry(sideLenght, sideLenght, sideLenght, sideDivision, sideDivision, sideDivision);
var attrPhi = new Float32Array( cubeGeom.attributes.position.count );
var attrTheta = new Float32Array( cubeGeom.attributes.position.count );
var attrSpeed = new Float32Array( cubeGeom.attributes.position.count );
var attrAmplitude = new Float32Array( cubeGeom.attributes.position.count );
var attrFrequency = new Float32Array( cubeGeom.attributes.position.count );
for (var attr = 0; attr < cubeGeom.attributes.position.count; attr++){
attrPhi[attr] = Math.random() * Math.PI * 2;
attrTheta[attr] = Math.random() * Math.PI * 2;
attrSpeed[attr] = THREE.Math.randFloatSpread(6);
attrAmplitude[attr] = Math.random() * 5;
attrFrequency[attr] = Math.random() * 5;
}
cubeGeom.addAttribute( 'phi', new THREE.BufferAttribute( attrPhi, 1 ) );
cubeGeom.addAttribute( 'theta', new THREE.BufferAttribute( attrTheta, 1 ) );
cubeGeom.addAttribute( 'speed', new THREE.BufferAttribute( attrSpeed, 1 ) );
cubeGeom.addAttribute( 'amplitude', new THREE.BufferAttribute( attrAmplitude, 1 ) );
cubeGeom.addAttribute( 'frequency', new THREE.BufferAttribute( attrFrequency, 1 ) );
和THREE.ShaderMaterial()
:
var vertexShader = [
"uniform float interpolation;",
"uniform float radius;",
"uniform float time;",
"attribute float phi;",
"attribute float theta;",
"attribute float speed;",
"attribute float amplitude;",
"attribute float frequency;",
"vec3 rtp2xyz(){ // the magic is here",
" float tmpTheta = theta + time * speed;",
" float tmpPhi = phi + time * speed;",
" float r = sin(time * frequency) * amplitude * sin(interpolation * 3.1415926);",
" float x = sin(tmpTheta) * cos(tmpPhi) * r;",
" float y = sin(tmpTheta) * sin(tmpPhi) * r;",
" float z = cos(tmpPhi) * r;",
" return vec3(x, y, z);",
"}",
"void main(){",
" vec3 newPosition = mix(position, normalize(position) * radius, interpolation);",
" newPosition += rtp2xyz();",
" vec4 mvPosition = modelViewMatrix * vec4( newPosition, 1.0 );",
" gl_PointSize = 1. * ( 1. / length( mvPosition.xyz ) );",
" gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n");
var fragmentShader = [
"uniform vec3 color;",
"void main(){",
" gl_FragColor = vec4( color, 1.0 );",
"}"
].join("\n");
var uniforms = {
interpolation: { value: slider.value},
radius: { value: 7.5},
color: { value: new THREE.Color(0x00ff00)},
time: { value: 0 }
}
var shaderMat = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
//wireframe: true //just in case, if you want to use THREE.Mesh() instead of THREE.Points()
});
如您所见,所有的魔法都发生在顶点着色器及其rtp2xyz()
函数中。
最后,动画功能的代码:
var clock = new THREE.Clock();
var timeVal = 0;
render();
function render(){
timeVal += clock.getDelta();
requestAnimationFrame(render);
uniforms.time.value = timeVal;
uniforms.interpolation.value = slider.value;
renderer.render(scene, camera);
}
哦,是的,我们的页面中有一个滑块控件:
<input id="slider" type="range" min="0" max="1" step="0.01" value="0.5" style="position:absolute;width:300px;">
这是一个片段
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(10, 10, 20);
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var vertexShader = [
"uniform float interpolation;",
"uniform float radius;",
"uniform float time;",
"attribute float phi;",
"attribute float theta;",
"attribute float speed;",
"attribute float amplitude;",
"attribute float frequency;",
"vec3 rtp2xyz(){ // the magic is here",
" float tmpTheta = theta + time * speed;",
" float tmpPhi = phi + time * speed;",
" float r = sin(time * frequency) * amplitude * sin(interpolation * 3.1415926);",
" float x = sin(tmpTheta) * cos(tmpPhi) * r;",
" float y = sin(tmpTheta) * sin(tmpPhi) * r;",
" float z = cos(tmpPhi) * r;",
" return vec3(x, y, z);",
"}",
"void main(){",
" vec3 newPosition = mix(position, normalize(position) * radius, interpolation);",
" newPosition += rtp2xyz();",
" vec4 mvPosition = modelViewMatrix * vec4( newPosition, 1.0 );",
" gl_PointSize = 1. * ( 1. / length( mvPosition.xyz ) );",
" gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n");
var fragmentShader = [
"uniform vec3 color;",
"void main(){",
" gl_FragColor = vec4( color, 1.0 );",
"}"
].join("\n");
var uniforms = {
interpolation: { value: slider.value},
radius: { value: 7.5},
color: { value: new THREE.Color(0x00ff00)},
time: { value: 0 }
}
var sideLenght = 10;
var sideDivision = 50;
var cubeGeom = new THREE.BoxBufferGeometry(sideLenght, sideLenght, sideLenght, sideDivision, sideDivision, sideDivision);
var attrPhi = new Float32Array( cubeGeom.attributes.position.count );
var attrTheta = new Float32Array( cubeGeom.attributes.position.count );
var attrSpeed = new Float32Array( cubeGeom.attributes.position.count );
var attrAmplitude = new Float32Array( cubeGeom.attributes.position.count );
var attrFrequency = new Float32Array( cubeGeom.attributes.position.count );
for (var attr = 0; attr < cubeGeom.attributes.position.count; attr++){
attrPhi[attr] = Math.random() * Math.PI * 2;
attrTheta[attr] = Math.random() * Math.PI * 2;
attrSpeed[attr] = THREE.Math.randFloatSpread(6);
attrAmplitude[attr] = Math.random() * 5;
attrFrequency[attr] = Math.random() * 5;
}
cubeGeom.addAttribute( 'phi', new THREE.BufferAttribute( attrPhi, 1 ) );
cubeGeom.addAttribute( 'theta', new THREE.BufferAttribute( attrTheta, 1 ) );
cubeGeom.addAttribute( 'speed', new THREE.BufferAttribute( attrSpeed, 1 ) );
cubeGeom.addAttribute( 'amplitude', new THREE.BufferAttribute( attrAmplitude, 1 ) );
cubeGeom.addAttribute( 'frequency', new THREE.BufferAttribute( attrFrequency, 1 ) );
var shaderMat = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
//wireframe: true
});
var points = new THREE.Points(cubeGeom, shaderMat);
scene.add(points);
var clock = new THREE.Clock();
var timeVal = 0;
render();
function render(){
timeVal += clock.getDelta();
requestAnimationFrame(render);
uniforms.time.value = timeVal;
uniforms.interpolation.value = slider.value;
renderer.render(scene, camera);
}
body{
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<input id="slider" type="range" min="0" max="1" step="0.01" value="0.5" style="position:absolute;width:300px;">