用webgl写一个地球自转的demo
时间: 2024-03-13 13:24:45 浏览: 162
好的,下面是一个简单的用 WebGL 实现地球自转的 Demo:
首先,需要在 HTML 文件中创建一个 canvas 元素:
```html
<canvas id="myCanvas" width="800" height="600"></canvas>
```
然后,在 JavaScript 文件中获取 canvas 元素,并创建 WebGL 上下文:
```javascript
const canvas = document.getElementById('myCanvas');
const gl = canvas.getContext('webgl');
```
接下来,需要编写着色器代码。这里我们需要两个着色器,一个用于顶点着色,一个用于片元着色。着色器代码如下:
```glsl
// 顶点着色器代码
attribute vec3 aPosition;
attribute vec2 aTexCoord;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying vec2 vTexCoord;
void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
}
// 片元着色器代码
precision mediump float;
uniform sampler2D uSampler;
varying vec2 vTexCoord;
void main() {
gl_FragColor = texture2D(uSampler, vTexCoord);
}
```
接着,创建着色器程序,并将着色器代码编译链接:
```javascript
// 顶点着色器代码
const vertexShaderSource = `
attribute vec3 aPosition;
attribute vec2 aTexCoord;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying vec2 vTexCoord;
void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
}
`;
// 片元着色器代码
const fragmentShaderSource = `
precision mediump float;
uniform sampler2D uSampler;
varying vec2 vTexCoord;
void main() {
gl_FragColor = texture2D(uSampler, vTexCoord);
}
`;
// 创建着色器程序
const shaderProgram = gl.createProgram();
// 编译链接顶点着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
gl.attachShader(shaderProgram, vertexShader);
// 编译链接片元着色器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
gl.attachShader(shaderProgram, fragmentShader);
// 链接着色器程序
gl.linkProgram(shaderProgram);
```
然后,需要创建一个地球的顶点数据和纹理数据。这里我们使用了球体的顶点数据和 uv 坐标,以及地球的纹理图像。顶点数据和纹理数据如下:
```javascript
// 球体顶点数据
const sphereVertices = [];
const sphereUVs = [];
const radius = 1;
const segments = 32;
const rings = 32;
for (let r = 0; r < rings + 1; r++) {
const theta = r * Math.PI / rings;
const sinTheta = Math.sin(theta);
const cosTheta = Math.cos(theta);
for (let s = 0; s < segments + 1; s++) {
const phi = s * 2 * Math.PI / segments;
const sinPhi = Math.sin(phi);
const cosPhi = Math.cos(phi);
const x = cosPhi * sinTheta;
const y = cosTheta;
const z = sinPhi * sinTheta;
sphereVertices.push(radius * x, radius * y, radius * z);
sphereUVs.push(s / segments, r / rings);
}
}
// 地球纹理图像
const textureImage = new Image();
textureImage.src = 'earth.jpg';
```
接下来,需要将顶点数据和纹理数据上传到 WebGL 中,并为着色器程序中的变量赋值:
```javascript
// 将顶点数据和纹理数据上传到 WebGL 中
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sphereVertices), gl.STATIC_DRAW);
const uvBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sphereUVs), gl.STATIC_DRAW);
// 为着色器程序中的变量赋值
const aPosition = gl.getAttribLocation(shaderProgram, 'aPosition');
gl.enableVertexAttribArray(aPosition);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
const aTexCoord = gl.getAttribLocation(shaderProgram, 'aTexCoord');
gl.enableVertexAttribArray(aTexCoord);
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);
const uModelViewMatrix = gl.getUniformLocation(shaderProgram, 'uModelViewMatrix');
const uProjectionMatrix = gl.getUniformLocation(shaderProgram, 'uProjectionMatrix');
const uSampler = gl.getUniformLocation(shaderProgram, 'uSampler');
// 加载纹理图像
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, textureImage);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
```
最后,需要在循环中更新地球的旋转角度,并渲染场景:
```javascript
let angle = 0;
function render() {
// 更新角度
angle += 0.01;
// 计算模型视图矩阵和投影矩阵
const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [0, 0, -5]);
mat4.rotateY(modelViewMatrix, modelViewMatrix, angle);
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, 45 * Math.PI / 180, canvas.width / canvas.height, 0.1, 100);
// 清空画布并绘制地球
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(shaderProgram);
gl.uniformMatrix4fv(uModelViewMatrix, false, modelViewMatrix);
gl.uniformMatrix4fv(uProjectionMatrix, false, projectionMatrix);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(uSampler, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, sphereVertices.length / 3);
requestAnimationFrame(render);
}
render();
```
这样,一个简单的地球自转的 WebGL Demo 就完成了。完整代码如下:
阅读全文