ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

WebGL之绘制三维地球

2021-04-26 02:33:09  阅读:248  来源: 互联网

标签:贴图 const WebGL 三维 vec3 顶点 gl 绘制 着色器


通过Three.js也许可以很方便的展示出3D模型,但是你知道它是怎么一步一步从构建网格到贴图到最终渲染出3D模型的吗?现在我们直接使用底层的webgl加上一点点的数学知识就可以实现它。

本节实现的效果: WebGL三维地球
WebGL三维地球

内容大纲

  1. 构建网格

  2. 编写着色器

  3. 实现3D地球

构建网格

首先我们要建立球体的三维模型,三维网格模型包括如下属性(不熟悉请复习webgl教程):

  • 顶点(position)
  • 法线(normal)
  • 贴图坐标(uv)
  • 顶点索引(indices)

最后要构建出如下所示的经纬球模型

经纬球

首先可以从xy平面构建圆形,接着再从xz平面将圆形转化为圆球,这其中只需使用到三角函数而已,是不是非常简单。

  • 法线使用的是顶点坐标,因为法线与顶点其实方向是一致的
  • 顶点索引为6个点,是因为每个面由两个三角形构成
  • 贴图uv坐标不需要深度信息,它对应上贴图的xy坐标即可

下面就是构建网格模型的基本逻辑:

const radius = 8;//半径
const n = 20;//经纬度格数
const position = [];//顶点
const normal = [];//法线
const texcoord = [];//uv坐标
const indices = [];//顶点索引
let x, y, z;

for (let i = 0; i < n; i++) {
  const rad = Math.PI / n * i - Math.PI / 2;//从-90度开始计算
  const r = radius * Math.cos(rad);
  y = radius * Math.sin(rad);
  for (let j = 0; j < n; j++) {
    x = r * Math.sin(xRadian * j);
    z = r * Math.cos(xRadian * j);
    position.push(x, y, z);
    texcoord.push(j / n, i / n);
    normal.push(x, y, z); //顶点作为法线,法线从圆心360度放射
    const c = i * (n + 1) + j
    indices.push(c, c + 1, c + l + 1, c, c + l + 1, c + l);//平面的索引
  }
}

编写着色器

和普通着色器相比,只是增加了uv坐标,uv直接通过顶点着色器差值透传到片段着色器即可,在片段着色器使用texture2D函数获取uv坐标对应的颜色,整体上也是比较基础。

// 顶点着色器
attribute vec4 aPosition;
attribute vec4 aNormal;
attribute vec2 aTexcoord;
uniform mat4 modelMatrix;
uniform mat4 vpMatrix;
varying vec3 fragPos;
varying vec3 fragNor;
varying vec2 texcoord;

void main() {
    gl_Position = vpMatrix * modelMatrix * aPosition;
    fragPos= vec3(modelMatrix * aPosition);
    fragNor = vec3(modelMatrix * aNormal);
    texcoord = aTexcoord;
}

// 片段着色器
precision mediump float;
uniform vec3 viewPos;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 ambientColor;
uniform sampler2D diffMap;
varying vec3 fragPos;
varying vec3 fragNor;
varying vec2 texcoord;

void main() {
    vec3 normal = normalize(fragNor);
    vec3 color = texture2D(diffMap, texcoord).rgb;
	
    // 光线方向
    vec3 lightDir = normalize(lightPos - fragPos);
    // 光线方向和法向量夹角
    float cosTheta = max(dot(lightDir, normal), 0.0);
    // 漫反射
    vec3 diffuse = lightColor * color * cosTheta;
	
    // 环境光
    // ...
    // 高光
    // ...

    gl_FragColor = vec4(ambient + diffuse + specular, 1.0);
}

实现3D地球

最后实现部分就和之前的webgl基本逻辑一致,不过要准备好地球贴图

地图

图片加载完将构建好的贴图sampler传入着色器即可,其他都是基础业务逻辑,不再详述,这样我们就将三维地球实现了

//...
const vpMatrix = m4.identity();
const uniforms = {
   modelMatrix: m4.identity(),
   lightPos: [20, 0, -20],
   lightColor: [1, 1, 1],
   ambientColor: [0.5, 0.5, 0.5],
};

gl.clearColor(0.1, 0.1, 0.1, 1);
gl.enable(gl.DEPTH_TEST);//深度测试
gl.enable(gl.CULL_FACE);//背面剔除
gl.viewport(0, 0, canvas.width, canvas.height); //设置绘图区域
gl.useProgram(program.program);

function animate() {
   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
   m4.multiply(projection, m4.inverse(m4.lookAt(eye, [0, 0, 0], [0, 1, 0])), vpMatrix);
   setBuffersAndAttributes(gl, vao);
   setUniforms(program, { vpMatrix });
   drawBufferInfo(gl, vao);
   gl.bindVertexArray(null);

   requestAnimationFrame(animate);
};

//加载贴图后执行
createTexture(gl, { src: '/img/earth.jpg', flipY: true }, texture => {
   uniforms.diffMap = texture;
   setUniforms(program, uniforms );
   animate();
}); 

标签:贴图,const,WebGL,三维,vec3,顶点,gl,绘制,着色器
来源: https://www.cnblogs.com/edwardloveyou/p/14700684.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有