没看过的朋友,记得先去看下入门一。
基础代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {
margin: 0;
padding: 0;
}
canvas {
width: 100%;
height: 100%
}
</style>
</head>
<body>
</body>
<script src="./three.js">
</script>
<script>
const vshader = `
varying vec2 v_uv;
void main() {
v_uv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`
const fshader = `
void main(){
gl_FragColor=vec4(1.0,0.0,0.0,1.0);
}
`;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/ window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const uniforms = {
}
const geometry = new THREE.PlaneGeometry(2, 2);
const material = new THREE.ShaderMaterial({
vertexShader: vshader,
fragmentShader: fshader,
uniforms
});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
camera.position.z = 3;
onWindowResize();
animate();
window.addEventListener("resize", onWindowResize, false);
function onWindowResize(event) {
camera.aspect = window.innerWidth/ window.innerHeight
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth,window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
</script>
</html>
在平面上加载图片。
const fshader = `
uniform sampler2D u_tex;
varying vec2 v_uv;
void main(){
vec3 color =texture2D(u_tex,v_uv ).rgb;
gl_FragColor=vec4(color,1.0);
}
`;
const uniforms = {
u_tex: { value: new THREE.TextureLoader().load("./target.png") }
}
之前一直用的正交相机,之后都用透视相机了。
sampler2D 2D纹理,仅能作为uniform变量,一般就是你传的图片。
texture2D 参数有很多钟,目前使用的参数为 sampler2D 和 uv。[0,0]和[1,1]对应的颜色值及从左下角的颜色到右上角颜色。返回vec4.
我使用的图片是1200、741.图片撑满平面导致被拉伸了,我想要图片不被拉伸。且多余的平面不展示。
const fshader = `
uniform sampler2D u_tex;
varying vec2 v_uv;
const float aspect = 1200.0/741.0;
void main(){
vec2 uv= vec2(v_uv.x,v_uv.y*aspect);
if(uv.y > 1.0){
discard;
}
vec3 color =texture2D(u_tex,uv ).rgb;
gl_FragColor=vec4(color,1.0);
}
`;
discard 丢弃片段,直接不渲染你不需要的点。
做一个图片切换的效果。
const fshader = `
uniform sampler2D u_tex;
uniform sampler2D u_tex2;
uniform float rate;
varying vec2 v_uv;
void main(){
// vec3 color1 =texture2D(u_tex,v_uv ).rgb;
// vec3 color2 =texture2D(u_tex2,v_uv ).rgb;
vec4 color = mix(texture2D(u_tex,v_uv ),texture2D(u_tex2,v_uv ),rate);
gl_FragColor=vec4(color.rgb,1.0);
}
`;
const uniforms = {
u_tex: { value: new THREE.TextureLoader().load("./target.png") },
u_tex2: { value: new THREE.TextureLoader().load("./1.jpg") },
rate:{value:0}
}
let speed =0.005;
function animate() {
requestAnimationFrame(animate);
uniforms.rate.value+=speed;
if(uniforms.rate.value>1 || uniforms.rate.value<0){
speed *=-1
}
renderer.render(scene, camera);
}
mix(a,b,c) = a*(1-c)+b*c;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {
margin: 0;
padding: 0;
}
canvas {
width: 100%;
height: 100%
}
</style>
</head>
<body>
</body>
<script src="./three.js">
</script>
<script src="./OrbitControls.js">
</script>
<script>
const vshader = `
varying vec2 v_uv;
void main() {
v_uv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`
const fshader = `
varying vec2 v_uv;
void main(){
gl_FragColor=vec4(v_uv,0.0,1.0);
}
`;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/ window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const uniforms = {
}
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.ShaderMaterial({
vertexShader: vshader,
fragmentShader: fshader,
uniforms
});
const controls = new THREE.OrbitControls(camera, renderer.domElement);
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
camera.position.z = 3;
onWindowResize();
animate();
window.addEventListener("resize", onWindowResize, false);
function onWindowResize(event) {
camera.aspect = window.innerWidth/ window.innerHeight
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth,window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
</script>
</html>
代码重置,加入轨道控制器。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {
margin: 0;
padding: 0;
}
canvas {
width: 100%;
height: 100%
}
</style>
</head>
<body>
</body>
<script src="./three.js">
</script>
<script src="./OrbitControls.js">
</script>
<script>
const vshader = `
varying vec2 v_uv;
void main() {
v_uv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`
const fshader = `
varying vec2 v_uv;
void main(){
gl_FragColor=vec4(v_uv.x,0.0,0.0,1.0);
}
`;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/ window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const uniforms = {
}
const geometry = new THREE.CylinderGeometry( 5, 5, 20, 4,1,true );
const material = new THREE.ShaderMaterial({
vertexShader: vshader,
fragmentShader: fshader,
uniforms,
side: THREE.DoubleSide,
});
const controls = new THREE.OrbitControls(camera, renderer.domElement);
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
camera.position.z = 50;
onWindowResize();
animate();
window.addEventListener("resize", onWindowResize, false);
function onWindowResize(event) {
camera.aspect = window.innerWidth/ window.innerHeight
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth,window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
</script>
</html>
这是一个只有四个面的圆柱,接下来让每个面显示不同颜色。
const fshader = `
uniform vec3 color[4];
uniform float faceCount;
varying vec2 v_uv;
void main(){
int index= int(mod(ceil(v_uv.x*faceCount),4.0) );
gl_FragColor=vec4(color[index],1.0);
}
`;
const uniforms = {
color:{value:[
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(0, 0, 1),
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(1, 1, 0),
]},
faceCount:{value:4}
}
color[4] 代表 color 是一个数组 每个元素都为vec3,声明时必须给定长度 对应的uniforms的 color:{value:[...]}这里传入数组即可。
int 强制类型转换。
mod (x,y) x - y * floor(x/y)。
下一节将开始讲解顶点着色器了。