初识shader

最近打算学习实现一个水面倒影的游戏场景效果,参考的教程里面用到了shader相关的知识,我好像基本没有任何的3d图形编程基础,于是决定补习一下相关的概念,并在本文记录。

<!--more-->

参考

1. 引子

shader中文名叫着色器,顾名思义,它的作用可以先简单理解为给屏幕上的物体画上颜色。而什么东西负责给屏幕上画颜色?当然是GPU(Graphic Processing Unit图形处理单元),所以我们写shader的目的就是告诉GPU往屏幕哪里画、怎么画。

着色器是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器之间不能相互通信,它们之间唯一的沟通只有通过输入和输出(有点像函数式编程?

着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。

在学习着色器程序编写之前,先了解一下着色器程序是如何加载并运行是很有必要的。

1.1. webgl代码示例

作为前端,从WebGL开始学习应该是比较快的,参考:WebGL 教程 MDN

绘制3D图形的伪代码,不用管具体的API,了解大概的流程就行,后面会介绍相关的概念

首先canvas获取webgl绘制上下文

const gl = canvas.getContext("webgl");

然后编写着色器程序代码,两种类型的着色器,后面会提到

// 顶点着色器,代码放在字符串内,具体的代码这里省略了
const vsSource = `` 
// 片段着色器
const fsSource = ``

编译编译着色器程序源码

// loaderShader 是一个自定义函数,封装createShader、shaderSource、compileShader等Api
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

然后挂载着色器程序

const shaderProgram = gl.createProgram();
// 创建着色器程序
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

现在,着色器程序就创建好了!

程序有了,现在就需要参数,参数就是一堆顶点,需要通过buffer缓存保存

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 顶点
const positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

好了,现在终于可以绘制了

// 跟canvas2d上下文一下,每一帧都需要擦掉重新画
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

// 设置透视矩阵,这是一种用于模拟相机透视失真的特殊矩阵
const projectionMatrix = mat4.create();
const modelViewMatrix = mat4.create();

// 从buffer处提取数据
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(...args)
gl.enableVertexAttribArray(...args)

// 绑定正方形的顶点缓冲到上下文
setPositionAttribute(gl, buffers, programInfo);

// 使用着色器程序
gl.useProgram(programInfo.program);
// 设置着色器变量
gl.uniformMatrix4fv(programInfo.uniformLocations.projectionMatrix,false,projectionMatrix);
gl.uniformMatrix4fv(programInfo.uniformLocations.modelViewMatrix,false,modelViewMatrix,);

// 画出对象
gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);

总之,大概流程看起来就是在主程序里面创建着色器程序源码(一堆代码字符串),编译着色器,然后传入参数并运行着色器程序,最后绘制出内容。

1.2. threejs示例

与原生的webgl想必,通过threejs的代码可以更容易地编写3D程序

import * as THREE from 'three';

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 geometry = new THREE.BoxGeometry( 1, 1, 1 );
// 材质
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );

// 网格
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

camera.position.z = 5;

renderer.render( scene, camera );

上面的代码中使用的是材质是默认的MeshBasicMaterial,可以通过RawShaderMaterial创建着色器材质

const vsSource = `
  uniform mat4 projectionMatrix;
  uniform mat4 viewMatrix;
  uniform mat4 modelMatrix;

  attribute vec3 position;

  void main() {
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
  }
`;

const fsSource = `
  precision mediump float;

  void main(){
    gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
  }
`;

var material = new THREE.RawShaderMaterial({
  vertexShader: vsSource,
  fragmentShader: fsSource
});

threejs中还有另外一种材质ShaderMaterial也支持着色器,这里不展开。

1.3. 着色器概念

接下来看一下着色器的完整概念。

着色器是使用 OpenGL ES 着色语言(*GLSL *,OpenGL Shading Language) 编写的程序,它携带着绘制形状的顶点信息以及构造绘制在屏幕上像素的所需数据,换句话说,它负责记录着像素点的位置和颜色。

有两种不同的着色器函数,顶点着色器(Vertex Shader和片段着色器(Fragment Shader),它们在渲染管线中扮演不同的角色,有以下区别:

  1. 作用范围:
    • 顶点着色器:顶点着色器是渲染管线的第一个阶段,它处理输入的顶点数据,通常用于执行顶点变换、法线变换、光照计算等。每个顶点都会经过顶点着色器。
    • 片段着色器:片段着色器是渲染管线的最后一个阶段,在每个像素或片段上执行。它通常用于计算最终的像素颜色,包括光照、纹理映射、阴影计算等。
  2. 输入数据:
    • 顶点着色器:接收顶点数据作为输入,如顶点位置、法线、颜色等。通常,顶点着色器的输出会传递给片段着色器。
    • 片段着色器:接收由顶点着色器传递过来的插值数据(如顶点颜色、法线、纹理坐标等),以及像素相关的信息(如光照、视角、材质属性等)作为输入。
  3. 输出数据:
    • 顶点着色器:通常不直接影响最终渲染的颜色,而是将顶点的位置、法线等数据传递给后续阶段,可以在这个阶段进行坐标变换、光照计算等。
    • 片段着色器:计算最终的像素颜色,它的输出通常是一个颜色值,用于渲染像素的最终颜色。
  4. 执行频率:
    • 顶点着色器:对于每个顶点执行一次。
    • 片段着色器:对于每个像素或片段执行一次。

总的来说,顶点着色器和片段着色器在图形渲染中扮演不同的角色,前者主要处理顶点级的数据和变换,而后者主要负责像素级的颜色计算。它们共同协作以创建最终的渲染图像。

2. 着色器语法

现在,可以开始动手来学习着色器的语法,以及编写简单的着色器程序了。

GLSL作为一门编程语言,也有固定的语法,其语法跟C语言有点相似。

  • 分号结尾
  • 缩进没有限制
  • 强类型

跟其他编程语言不同的是,GLSL貌似没有内置的输出,如果要调试GLSL代码,可以找一个GLSL在线运行网站,或者自己在本地运行上面的那段ThreeJS demo代码,通过修改vsSourcefsSource代码来调试。

参考:https://juejin.cn/post/7158032481302609950

2.1. 基础语法

基础数据类型

// 整形
int a = 1;
// 浮点
float pi  = 3.14;
// 布尔值
bool flag = true;

二维向量,具备x和y两个属性

vec2 v2 = vec2(1.0, 1.0);
v2.x = 1.1;
v2.y = 1.2;

三位向量,具备x、y、z三个属性

vec3 v3 = vec3(1.0, 1.0, 1.0);
v3.x = 1.1;
v3.y = 1.2;
v3.z = 1.3;

此外还有一些类型如 mat2mat3mat4等,这些向量和矩阵类型支持响应计算如向量相加、矩阵乘积等。

控制流,与C语言基本一致

if (condition) {
    // 代码块
} else {
    // 代码块
}

for (int i = 0; i < 10; i++) {
    // 循环体
}

while (condition) {
    // 循环体
}

函数,需要声明返回值的内容,返回值为空声明为void

float add(float a, float b) {
  return a + b;
}

GLSL内置了很多函数,大多数与数学计算相关,比如absmaxfloor等。

每个着色器程序都有一个main函数

// ...辅助函数、变量
void main(){ 
    // ... 程序
}

在变量声明的类型前面,经常还会跟一个关键字如attributeuniform等,这种被称为存储限定符。

变量命名规则

  • 与C语言的变量规则相似
  • gl_开头的为内置变量,比如gl_Position
  • webgl__webgl 是着色器保留字,不能取这两个开头的变量名

2.2. 顶点着色器

attribute是属性限定变量,顶点着色器只有一段代码,但是这段代码会在每个顶点运行,因此位置属性变量attribute是所有顶点之间唯一会发生变化的变量,通过下面方式获取程序运行时的顶点位置,这个值是在ThreeJS中内置设置的

attribute vec3 position;

uniform是矩阵限定变量,每个矩阵将转换 position,直到我们获得最终的裁切空间坐标,对于每个顶点而言他们都是相同的,这些值也是在ThreeJS中内置设置的。

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

gl_Position 是顶点着色器中用于指定顶点位置的内置变量,类型为vec4,可以对其属性进行赋值,从而达到修改顶点位置的效果

下面是一段常用的顶点着色器写法

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
attribute vec3 position;

void main(){
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  // modelPosition.y += 2.0; // 将整个图像向上移动
  vec4 viewPosition = viewMatrix * modelPosition;
  vec4 projectedPosition = projectionMatrix * viewPosition;
  gl_Position = projectedPosition; // 修改
}

整体感觉顶点着色器就是控制每个顶点的位置的?

2.3. 片段着色器

片段着色器控制每个可见像素的颜色。

片段着色器顶部一般有一段描述浮点精度的代码,其中有highpmediumplowp,中间的mediump是最常用的。

precision mediump float;

gl_FragColor是片元着色器中用于指定片元颜色的一个内置变量,用来设置像素颜色,类型为vec4

// 片元着色器
void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

2.4. attribute

外部主程序可以通过attribute将数据传递给顶点着色器,attribute用于在顶点着色器中传递每个顶点特有的数据,通常用于传递顶点的位置、颜色等信息。

const geometry = new THREE.BoxGeometry(1, 1, 1)
const count = geometry.attributes.position.count
const randoms = new Float32Array(count)
for (let i = 0; i < count; i++) {
  randoms[i] = Math.random()
}
// 第二个参数表示每个顶点使用randoms数组中的一个数据
geometry.setAttribute('params1', new THREE.BufferAttribute(randoms, 1))

对应的顶点着色器中,通过attribute可以使用传入的参数

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
attribute vec3 position;
attribute float params1; // 这里就可以获取传入的参数

void main(){
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  modelPosition.z += params1 * 0.1; // 可以使用
  vec4 viewPosition = viewMatrix * modelPosition;
  vec4 projectedPosition = projectionMatrix * viewPosition;
  gl_Position = projectedPosition;
}

2.5. varying

如果想要把顶点着色器中的数据传递给片段着色器,需要通过varying

首先在顶点着色器中通过varying创建一个变量,然后将attribute的数据赋值给它,(貌似这个变量一般以v开头,但是我测试了好像常规的变量名也可以

attribute float params1;
varying float params2; // 声明要传递给片段着色器的数据

void main() {
  // ...
  params2 = params1; // 将外部程序传入的attribute赋值给params2
}

然后在片段着色器中,同样通过varying创建一个同名变量

precision mediump float;
varying float params2; // 跟顶点着色器里面的声明相同

void main(){
  // 使用params2
    gl_FragColor = vec4(1.0, params2, 1.0, 1.0);
}

2.6. uniform

外部程序除了通过attribute向顶点着色器传值(初始值,顶点着色器在每次执行的之后可能会发生变化),还可以通过uniform传递一些在所有着色器程序(包括顶点着色器和片段着色器)都不会变化的值(类似于全局常量。

比如某个着色器

uniform float uTest; // 定义uTest
void main(){
  // 使用uTest
}

可以通过uniforms参数传入uTest的值

var material = new THREE.RawShaderMaterial({
  vertexShader: vsSource,
  fragmentShader: fsSource,
  uniforms: {
    uTest: { value: 1 } // 传入
  }
});

通过uniform可以很方便地控制着色器程序中的代码,像函数的参数一样。

2.7. 小结

着色器的语法感觉还是比较简单的,难点在于实现各种绘图效果。

在学习的时候发现书架上还有一本《计算机图形学(第4版)》,这本绿皮的大头书我翻开一看,着色器在24个章节中只占据了短短1个章节,任重而道远啊~

3. 一些3D图形编程中概念

相较于原生的WebGL程序,threejs进行了大量封装,可以让我们以很少的工作完成3D编程,不过其中引入了很多概念,比如相机、材质、网格、模型等,这些名称在游戏引擎中也经常看到,因此接下来先学习一下相关的概念。(大部分内容由GPT友情提供,可以按需跳过

3.1. 缓冲区

缓冲区一般用来放置顶点参数,比如顶点坐标、颜色、纹理坐标等。将这些数据放在缓冲区主要是为了有效地传递这些数据到图形处理单元(例如,GPU)进行渲染。这样做有以下几个好处:

  1. 内存和性能优化: 将顶点参数存储在缓冲区中,可以将它们紧密地打包在内存中,减少内存分配和数据传输的开销。这有助于提高性能,特别是当处理大量顶点数据时,减少了CPU与GPU之间的数据传输次数。
  2. 并行处理: 现代图形处理单元(GPU)是高度并行化的,它们可以同时处理大量的数据。将顶点数据放在缓冲区中可以更好地利用GPU的并行处理能力,从而加速渲染过程。
  3. 数据复用: 缓冲区中的数据可以在多个渲染帧之间重复使用,而不必每次都重新传递数据。这对于渲染动画或交互式应用程序非常有用。
  4. 灵活性: 缓冲区中的数据可以用于不同的渲染任务,例如绘制不同的几何形状,而无需重新生成或传递数据。这提供了更大的灵活性和代码重用。

总之,将顶点参数存储在缓冲区中是为了优化性能、提高效率以及更好地利用现代图形硬件的特性。这种做法在图形编程中非常常见,特别是在使用WebGL、OpenGL或其他图形API时。

3.2. 3D模型

在实际业务中,创建3D模型通常是一个复杂而创造性的过程,需要使用专业的3D建模软件来进行。以下是一般的创建3D模型的步骤:

  1. 选择建模软件: 选择一款适合您需求的3D建模软件。一些常用的3D建模软件包括Blender、Autodesk Maya、3ds Max、Cinema 4D等。您可以根据您的需求和预算选择合适的工具。
  2. 学习建模技巧: 3D建模是一门复杂的艺术和技术,需要学习建模技巧和工具。通常,您可以参考在线教程、书籍、课程或社区论坛来学习。
  3. 概念设计: 在开始建模之前,通常需要制定一个概念设计。这包括确定模型的外观、形状、材质、细节等。您可以绘制草图或参考现有的概念设计。
  4. 建模: 使用建模软件创建3D模型。这通常涉及到在3D空间中绘制形状、操作顶点、边缘和面,以及应用纹理和材质。您可以使用多边形建模、NURBS建模、体素建模等不同技术。
  5. 纹理和材质: 为模型添加纹理、材质和贴图,以增加模型的外观和真实感。这通常涉及到绘制纹理贴图、指定反射率、透明度、光照等属性。
  6. UV映射: 对模型进行UV映射,以确定纹理如何映射到模型的表面。这是纹理贴图的关键步骤。
  7. 光照和渲染: 调整光照设置,以确保模型在渲染时看起来逼真。这包括设置光源、阴影、环境光照等。
  8. 动画(可选): 如果您需要创建动态的3D模型,可以使用动画技术来为模型添加动作和行为。
  9. 导出: 将模型导出为合适的3D文件格式,以便在您的应用程序或游戏中使用。常见的格式包括.glft、.obj、.fbx、.dae等。
  10. 集成到应用程序: 最后,将创建的3D模型集成到您的应用程序、游戏或项目中,并根据需要进行进一步的调整和优化。

需要注意的是,3D建模是一个需要时间和经验的过程,因此初学者可能需要一些时间来熟练掌握。同时,如果您需要高度复杂的3D模型,可能需要与专业的3D建模师合作,以确保最终的模型满足您的需求。

3.3. 相机Camera

相机是计算机图形学中的一个重要概念,与现实世界中的相机类似,用于模拟从某个视点观察场景的过程。

相机用来观察三维世界的窗口,可以将三维世界中的物体映射到二维图像平面上。相机的位置、方向、视角等参数决定了我们看到的物体的形状和大小。

以下是相机的一些关键概念:

  1. 视点(Viewpoint):视点是相机的位置和朝向,决定了观察者在三维场景中的视角。视点可以在场景中自由移动,以模拟不同的观察角度和位置。
  2. 投影(Projection):相机将三维场景投影到二维图像平面上,以便在屏幕上显示。两种常见的投影方式包括:
    • 透视投影(Perspective Projection):模拟人眼的透视效果,使远处的物体看起来较小,近处的物体看起来较大。
    • 正交投影(Orthographic Projection):保持物体在所有深度上的大小不变,不考虑透视效果。
  3. 镜头参数(Lens Parameters):这些参数包括焦距、视野角度、曝光等,影响相机的成像效果。不同的参数设置可以产生不同的视觉效果。
  4. 视锥体(View Frustum):视锥体是一个三维空间中的锥体,定义了相机可以看到的区域。只有位于视锥体内的对象才会被渲染到屏幕上,而位于视锥体之外的对象则不可见。
  5. 渲染(Rendering):一旦相机设置好,渲染引擎会使用相机的参数来计算场景中每个可见点的颜色、深度等信息,然后将这些信息渲染到屏幕上,生成最终的图像。
  6. 镜头效果(Lens Effects):与现实世界的相机类似,虚拟相机也可以模拟一些特殊的效果,如景深(Depth of Field)、运动模糊(Motion Blur)、光晕(Lens Flare)等,以增强渲染的逼真度和艺术效果。

在计算机图形学中,使用相机的概念可以让我们模拟和控制虚拟世界的观察和呈现过程,从而创建逼真的三维图像和动画。不同的相机参数和技术可以用来实现各种视觉效果,从逼真的渲染到抽象的艺术风格。

3.4. 材质Material

在计算机图形学中,材质(Material)是用来描述物体表面属性和外观的概念。材质定义了一个物体如何反射、吸收和传播光线,从而影响其视觉外观。材质在渲染三维场景时起到至关重要的作用,因为它们决定了物体在渲染图像中的外观。

以下是计算机图形学中材质的关键概念:

  1. 颜色属性(Color Properties):材质定义了物体的颜色属性,包括基本颜色、反射颜色、折射颜色等。这些颜色属性决定了物体在不同光照条件下的外观。
  2. 反射属性(Reflectance Properties):材质描述了物体对不同类型光线的反射方式。常见的反射属性包括:
    • 漫反射(Diffuse Reflection):漫反射材质会均匀地将光线反射到各个方向,不会产生明亮的高光。
    • 镜面反射(Specular Reflection):镜面反射材质会产生明亮的高光,反映了物体光滑表面的反射特性。
    • 环境反射(Ambient Reflection):环境反射材质模拟了物体表面吸收并再次散射光线的效应,增加了物体的整体亮度。
  3. 透明度(Opacity):材质可以定义物体的透明度,这决定了物体是否是透明的或半透明的。透明度属性允许光线穿过物体,创建玻璃、水、烟雾等效果。
  4. 贴图(Textures):材质可以包括贴图,这些贴图可以用来改变表面的颜色、纹理、法线(用于模拟表面的凹凸)等属性,以增加表面的细节和真实感。
  5. 折射(Refraction):一些材质可以模拟折射效果,当光线穿过不同折射率的材质时,会发生弯曲。这用于模拟玻璃、水、宝石等材质的效果。
  6. 表面光滑度(Surface Roughness):材质可以描述物体表面的光滑度,从完全光滑到非常粗糙的不同类型表面。这会影响镜面反射的外观。
  7. 法线映射(Normal Mapping):通过法线贴图,材质可以模拟物体表面的微小细节,使其在渲染时看起来更加复杂和真实。
  8. 材质属性(Material Properties):材质还可以定义其他属性,如光泽度、金属度、光照强度等,以进一步调整物体的外观。

材质的选择和设置对于创建逼真的三维场景非常重要。在计算机图形中,渲染引擎会根据材质属性、光照条件和相机视角来计算物体的外观,从而生成最终的图像。不同类型的材质和属性可以用来实现各种视觉效果,从模拟木材和金属到创造虚构的外星生物外观。

3.5. 几何体Geometry

几何体(Geometry)是描述三维空间中物体的形状和结构的数学模型。几何体通常由一组顶点、边和面组成,它们定义了物体的外观和拓扑结构。几何体是计算机图形中的基本构建块之一,用于创建和呈现三维模型。

以下是计算机图形学中几何体的关键概念:

  1. 顶点(Vertices):顶点是几何体的基本构建块,通常表示三维空间中的一个点。每个顶点由其空间坐标(x、y、z)定义。
  2. 边(Edges):边是连接两个顶点的线段,它们定义了几何体的拓扑结构。边界辅助定义了几何体的外形。
  3. 面(Faces):面是由三个或更多个相邻顶点组成的多边形,它们定义了几何体的表面。在三维图形中,最常见的面是三角形(三角网格)和四边形(四边网格)。
  4. 拓扑结构(Topology):几何体的拓扑结构指定了顶点、边和面之间的连接关系。正确的拓扑结构对于一些计算操作非常重要,如法线计算、碰撞检测等。
  5. 法线(Normals):法线是与面或顶点关联的矢量,用于描述表面的朝向。法线对于光照计算和渲染阶段非常重要,因为它们决定了物体如何反射光线。
  6. 纹理坐标(Texture Coordinates):纹理坐标是与顶点相关联的二维坐标,用于定位纹理图像上的点。它们用于将纹理映射到物体的表面,以增加表面的细节和外观。
  7. 几何体类型:几何体可以是各种类型,包括简单的基本几何体(如立方体、球体、圆柱体)、复杂的多边形网格、以及由数学公式生成的自定义几何体。

几何体在计算机图形学中起着非常重要的作用,它们用于表示物体的外观、形状和结构。几何体可以在3D建模软件中创建,然后加载到计算机图形程序中进行渲染和处理。在渲染过程中,几何体的顶点、边和面信息被用来计算光照、阴影、纹理映射等效果,以生成最终的三维图像。

3.6. 网格Mesh

在计算机图形学中,网格(Mesh)用于表示三维物体的几何形状。网格由一组顶点、边和面组成,它们以一种有序的方式组织在一起,以描述物体的形状和拓扑结构。网格是创建、处理和渲染三维模型的基本数据结构之一。

网格是几何体的实例,它将几何信息与外观属性(材质、颜色、纹理等)组合在一起,以便在渲染时呈现具体的三维模型。网格包括了几何体的结构信息以及用于渲染的外观属性。网格通常是最终被添加到场景中的对象,用于构建三维场景。

以下是计算机图形学中网格的关键概念:

  1. 顶点(Vertices):顶点是网格的基本构建块,通常表示三维空间中的一个点。每个顶点通常包括其空间坐标(x、y、z),以及可能的法线(用于光照计算)和纹理坐标(用于贴图)。顶点决定了物体的形状。
  2. 边(Edges):边是连接两个顶点的线段,它们定义了网格的拓扑结构。边界边界辅助定义了网格的外形。
  3. 面(Faces):面是由三个或更多个相邻顶点组成的多边形,它们定义了物体的表面。在三维图形中,最常见的面是三角形(三角网格)和四边形(四边网格)。
  4. 拓扑结构(Topology):网格的拓扑结构指定了顶点、边和面之间的连接关系。正确的拓扑结构对于一些计算操作非常重要,如法线计算、碰撞检测等。
  5. 法线(Normals):法线是与面或顶点关联的矢量,用于描述表面的朝向。法线对于光照计算和渲染阶段非常重要,因为它们决定了物体如何反射光线。
  6. 纹理坐标(Texture Coordinates):纹理坐标是与顶点相关联的二维坐标,用于定位纹理图像上的点。它们用于将纹理映射到物体的表面,以增加表面的细节和外观。
  7. 三角网格(Triangle Mesh):三角网格是最常见的网格类型,其中每个面都是一个三角形。它们在计算机图形中广泛使用,因为三角形是计算机图形学中的基本形状,且容易进行各种计算和渲染操作。

网格是三维模型的基础,可以用于创建各种物体,从简单的几何体到复杂的角色模型。计算机图形学中的建模工具和渲染引擎通常支持导入、创建、编辑和渲染网格,使图形设计师和开发人员能够创建逼真的三维场景和动画。

3.7. 天空盒Skybox

天空盒(Skybox)是一种用于模拟远处环境的技术,通常用于创建三维场景的背景。它是一个立方体纹理,每个面上都贴有不同的纹理图像,以模拟天空、山脉、城市或其他远景景观。这个立方体贴图围绕在观察者(通常是摄像机或视点)周围,以创建一种感觉就像是被包围在一个大型环境中的效果。

以下是关于天空盒的一些重要信息:

  1. 六个面:天空盒通常由一个立方体构成,有六个面,分别代表不同的方向(上、下、前、后、左、右)。每个面都有一个相应的纹理。
  2. 环境模拟:天空盒主要用于模拟场景中的远景环境,以增强三维场景的真实感和沉浸感。它可以让用户感觉自己置身于一个大型、开放的环境中,而不仅仅是一个封闭的三维空间。
  3. 视点固定:天空盒是与观察者的视点位置无关的。因此,当观察者在场景中移动时,天空盒保持固定在他们周围,给人一种无限远景的感觉。
  4. 用途:天空盒常见于游戏开发、虚拟现实、模拟器等应用中。它们用于提供背景景观,增强场景的外观和氛围。例如,在一个游戏中,天空盒可以展示一个美丽的日落、星空、城市风景或其他环境。
  5. 纹理图像:天空盒的纹理图像通常是由专业艺术家创建的,以确保视觉效果逼真。这些图像需要精心制作,以保持连续性和一致性,以便在立方体的各个面上流畅过渡。

总之,天空盒是计算机图形学中用于模拟远景环境的一种技术,通过一个立方体纹理来创建环境的感觉,以提高三维场景的视觉吸引力和真实感。它在虚拟世界中营造出更加引人入胜的环境。