所需的js文件,可以再該網站取得
https://www.wellyyss.cn/ysThree/main/app.html
相較於原文,簡化了一些shader,方便初學者理解
smoothstep函數的功能可以再shadertoy上進行測試
<script src="../../plugins/ys/ys.min.js"></script>
<script src="../../plugins/threeLibrary/three.min.js"></script>
<script src="../../plugins/threeLibrary/js/controls/OrbitControls.js"></script>
<script src="../../plugins/createJs/tween.min.js"></script>
<script src="../../plugins/threeLibrary/js/libs/stats.min.js"></script>
<script src="../../plugins/ysThree/ysThree-1.0.1.js"></script>
<script>
const el = document.getElementById('box')
const app = new YsThree(el,{
gridHelper:true,//網格參考線
axes:true,//座標輔助
clearColor:'#000'//畫布顏色
})
const camera = app.camera
const renderer = app.renderer
const scene = app.scene
const controls = app.initOrbitControls()
const clock = new THREE.Clock()
//add light
const directionalLight = new THREE.DirectionalLight( '#fff' )
directionalLight.position.set( 30, 30, 30 ).normalize()
scene.add( directionalLight )
const ambientLight = new THREE.AmbientLight('#fff',0.3) // obj 唯一 id
scene.add(ambientLight)
/* **** **** **** ****/
app.initStatus(Stats)
var vertexShader=[
'varying vec3 vColor;',
'varying vec3 vVertexNormal;',
"varying vec2 vUv;",
'varying float v_pz; ',
'void main(){',
' v_pz = position.y; ', //獲取頂點位置的y
' vVertexNormal = normal;', //頂點法向量---內置 http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/zh/renderers/webgl/WebGLProgram
' vColor = color;', //頂點顏色
' gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',//頂點位置
'}'
].join('\n')
var fragmentShader= [
'uniform float boxH;', //立方體高度,uniform傳入
'varying vec3 vVertexNormal;', //頂點法向量,由頂點着色器傳入--插值
'varying vec3 vColor;', //頂點顏色,由頂點着色器傳入--插值
"varying vec2 vUv;", //紋理座標,頂點着色器傳入
'varying float v_pz; ', //y的值,頂點着色器傳入
'float plot ( float pct){',//pct是box的高度,v_pz是y的值
'return smoothstep( pct-8.0, pct, v_pz) -', //(smoothstep(edge1,edge2,x))smoothstep函數定義從0到1之間由edge1和edge2上下邊界,x爲輸入值,返回插值
'smoothstep( pct, pct+0.02, v_pz);', //不在0-1範圍內的數會被歸一化到0和1內,越界會被設爲0/1
'}',
'void main(){',
'float f1 = plot(boxH);', //以當前盒子的高度(光效),和y的值計算出顏色
'vec4 b1 = mix(vec4(1.0,1.0,1.0,1.0),vec4(f1,f1,f1,1.0),0.8);',
'gl_FragColor = mix(vec4(vColor,1.0),b1,f1);',//混合兩種顏色
'gl_FragColor = vec4(gl_FragColor.r,gl_FragColor.g,gl_FragColor.b,0.9);',//重新設置片元顏色
'}'
].join('\n')
const ShaderBar = {
uniforms: {
boxH: { value: -10.0 },
},
vertexShader: vertexShader,
fragmentShader: fragmentShader
}
const material = new THREE.ShaderMaterial({
uniforms: ShaderBar.uniforms,
vertexShader: ShaderBar.vertexShader,
fragmentShader: ShaderBar.fragmentShader,
vertexColors: ShaderBar, //暫時未理解該處作用
});
material.needsUpdate = true
function addCube() {
for (let i = 0 ;i<100;i++){
const h = Math.random()*6 + 5
const cubeGeo = new THREE.BoxBufferGeometry(1, h, 1);
cubeGeo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(24 * 3), 3)); // setAttribute 以前是.addAttribute
// 相當於在 shader中創建了 attribute vec4 position
const colors1 = cubeGeo.attributes.color;
for (let i = 0; i < 24; i+=2) {
let r = Math.random()*0.8,g=Math.random()*0.7,b=Math.random()*0.5;
colors1.setXYZ(i, r, g, b);
colors1.setXYZ(i+1,r, g, b);
}
const k = 2;
colors1.setXYZ(k * 4 + 0, .0, 1.0, 1.0);
colors1.setXYZ(k * 4 + 1, .0, 1.0, 1.0);
colors1.setXYZ(k * 4 + 2, .0, 1.0, 1.0);
colors1.setXYZ(k * 4 + 3, .0, 1.0, 1.0);
const cube = new THREE.Mesh( cubeGeo,material)
cube.position.set(Math.random()*100 - 50,h / 2,Math.random()*100 - 50)
scene.add(cube)
}
}
addCube()
/* **** **** **** ****/
function render() {
controls.update(clock.getDelta())
renderer.render( scene,camera)
TWEEN.update()
app.staus.update()
ShaderBar.uniforms.boxH.value = ShaderBar.uniforms.boxH.value + 0.1
if( ShaderBar.uniforms.boxH.value > 30){
ShaderBar.uniforms.boxH.value = -10.0
}
requestAnimationFrame(render)
}
render()
</script>