一、原理
球面坐标公式:
当然按照y轴向上的习惯,x,y,z与图上不一致,转换过来即
x=r*sin(θ)cos(φ)
y=r*cos(θ)
z=r*sin(θ)sin(φ)
坑
注意:索引数目较多需要用Uint16Array,相应地绘制时也需要修改对应类型
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
二、效果:
三、代码
//顶点着色器
var VSHADER_SOURCE = /*glsl*/`
attribute vec2 a_Uv;
attribute vec4 a_Position;
uniform mat4 u_ModelViewMatrix;
varying vec2 v_Uv;
void main(){
gl_Position = u_ModelViewMatrix*a_Position;
v_Uv=a_Uv;
}`;
//片元着色器
var FSHADER_SOURCE = /*glsl*/`
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D u_Texture;
varying vec2 v_Uv;
void main(){
gl_FragColor = texture2D(u_Texture,v_Uv);
}`;
//声明js需要的相关变量
var canvas = document.getElementById("canvas");
var gl = getWebGLContext(canvas);
async function main() {
if (!gl) {
console.log("你的浏览器不支持WebGL");
return;
}
//初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log("无法初始化着色器");
return;
}
// var n = initVertexBuffers(gl);
var n = drawSphere(gl)
if (n < 0) {
console.log("无法创建缓冲区");
return;
}
const texture = await initTexture(gl, './image/earth.jpg', 0)
//设置底色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
var currentAngle = 0
var tick = function () {
//计算角度
currentAngle -= 0.3
draw(gl, currentAngle, n)
requestAnimationFrame(tick)
}
tick()
//进入场景初始化
//draw(gl, n, u_ModelViewMatrix, u_ModelMatrix, u_NormalMatrix);
}
function draw(gl, currentAngle, n) {
//设置视角矩阵的相关信息(视点,视线,上方向)
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
//设置模型矩阵的相关信息
var modelMatrix = new Matrix4();
modelMatrix.setRotate(-23.5, 0, 0, 1);
modelMatrix.rotate(currentAngle, 0, 1, 0);
//设置透视投影矩阵
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width / canvas.height, 0.1, 100);
//计算出模型视图矩阵 viewMatrix.multiply(modelMatrix)相当于在着色器里面u_ViewMatrix * u_ModelMatrix
var modeViewMatrix = projMatrix.multiply(viewMatrix.multiply(modelMatrix));
//设置视角矩阵的相关信息
var u_ModelViewMatrix = gl.getUniformLocation(gl.program, "u_ModelViewMatrix");
if (u_ModelViewMatrix < 0) {
console.log("无法获取mvp矩阵变量的存储位置");
return;
}
//将试图矩阵传给u_ViewMatrix变量
gl.uniformMatrix4fv(u_ModelViewMatrix, false, modeViewMatrix.elements);
//开启隐藏面清除
gl.enable(gl.DEPTH_TEST);
//清空颜色和深度缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//绘制图形
// gl.drawElements(gl.LINES, n, gl.UNSIGNED_SHORT, 0);
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
}
function drawSphere(gl) {
const maxLat = 30, maxLon = 30
const radius = 1
const position = []
const uv = []
//计算所有的索引值
const indexArr = []
for (let lat = 0; lat <= maxLat; lat++) {
// //计算占据的百分比并转为弧度(0->PI之间)
// let lat=15
const theta = lat / maxLat * Math.PI
const y = radius * Math.cos(theta)
for (let lon = 0; lon < maxLon; lon++) {
//计算横向占据的百分比(0-2*PI之间)
const phi = lon * 2 * Math.PI / (maxLon - 1)
const x = radius * Math.sin(theta) * Math.cos(phi)
const z = radius * Math.sin(theta) * Math.sin(phi)
position.push(x, y, z)
uv.push(1 - lon / (maxLon - 1), 1 - lat / maxLat)
//计算当前索引个数
if (lat < maxLat) {
const current = lon + lat * maxLon
if (lon === maxLon - 1) {
continue;
} else {
indexArr.push(current, current + maxLon, current + 1)
indexArr.push(current + 1, current + maxLon, current + maxLon + 1)
}
}
}
}
const vertices = new Float32Array(position)
const indices = new Uint16Array(indexArr)
const uvs = new Float32Array(uv)
//创建缓冲区对象
initArrayBuffer(gl, vertices, 3, gl.FLOAT, "a_Position");
initArrayBuffer(gl, uvs, 2, gl.FLOAT, "a_Uv");
//将顶点索引数据写入缓冲区对象
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
return indices.length
}
function initArrayBuffer(gl, data, num, type, attribute) {
//创建缓冲区对象
var buffer = gl.createBuffer();
if (!buffer) {
console.log("无法创建缓冲区对象");
return -1;
}
//绑定缓冲区对象并写入数据
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
//获取顶点位置变量位置
var a_attribue = gl.getAttribLocation(gl.program, attribute);
if (a_attribue < 0) {
console.log("无法获取顶点位置的存储变量" + attribute);
return -1;
}
//对位置的顶点数据进行分配,并开启
gl.vertexAttribPointer(a_attribue, num, type, false, 0, 0);
gl.enableVertexAttribArray(a_attribue);
}