其他章节请看:
有人说三维模型的基本单元是三角形。比如复杂的游戏角色,也只是用许多三角形画出来的。
不管上述说法是否属实,本篇先把三角形画出来。
鼠标点击绘点示例我们写了这样的代码:
points.forEach(item => {
gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
gl.drawArrays(gl.POINTS, 0, 1);
})

这种方法一次只能绘制一个点。
比如需要绘制一个三角形,应该是一个连贯的动作。比如在顶点着色器中一次性画三个点,然后用线连接;而不是绘制一个点,在绘制一个点,在绘制一个点...,不应该是零散的
Tip:提前透露 - 只要一次性将三个点绘制出来,其实三角形也就画出来了。
一次性绘制多个顶点可以使用缓冲区对象。
可以使用 webgl 中的缓冲区对象(buffer object) 一次性的向着色器传入多个顶点数据。
使用缓冲区对象向顶点着色器传入多个顶点数据,需要如下5个步骤:
创建缓冲区对象绑定缓冲区对象写入数据到缓冲区对象分配缓冲区对象给一个 attribute 变量开启 attribute 变量
完整代码如下:
// 一次绘制三个点
const VSHADER_SOURCE = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
gl_PointSize = 10.0;
}
`
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main() {
const canvas = document.getElementById('webgl');
const gl = canvas.getContext("webgl");
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 顶点数据
const vertices = {
// 顶点数据。Float32Array 当做普通数组,对应 C 语言的 float
data: new Float32Array([
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
]),
// 顶点数
vertexNumber: 3,
// 每个顶点分量数,例如第一个点(0.0, 0.5)
count: 2,
}
// 将顶点的位置写入顶点着色器
initVertexBuffers(gl, vertices)
// gl.drawArrays(mode, first, count)
// 还是画点(gl.POINTS),从缓冲区的第一个位置(0)开始画,绘制3(vertices.vertexNumber)个点
gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber);
}
// 将三个顶点通过缓冲对象一次写入着色器
function initVertexBuffers(gl, {data, count}) {
// 1. 创建缓冲区对象
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('创建缓冲区对象失败');
return -1;
}
// 绑定缓冲区对象绑定到`目标`。只能通过目标向缓冲区写数据
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 将数据写入缓冲区
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
// 将整个缓冲区对象分配给 atttibute(a_Position) 变量
gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0);
// 激活 attribute 变量,使缓冲区对 attribute 变量分配生效
gl.enableVertexAttribArray(a_Position);
}
通过 initVertexBuffers() 将3个顶点分配给 attribute 变量,执行 gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber) 时,顶点着色器执行了3次。顶点着色器执行过程和缓冲区数据传入过程如下:

绘制出所有点后,颜色缓冲区的内容就会自动显示在浏览器中。
initVertexBuffers() 里面涉及了许多概念和api,请往下阅读。
绘制三维图形 webgl 需要处理大量相同的数据,例如顶点坐标。为了优化性能,就引入了一种特殊的数组(类型化数组),浏览器事先知道数组中的数据类型,处理起来就更有效率。
Float32Array 就是其中一种类型化数组,通常用于存储顶点坐标或颜色。
Tip: webgl 中很多操作都需要用到类型化数组。
webgl 中有如下类型化数组:
| 数组类型 | 每个元素所占字节数 | 对应C语言的数据类型 |
|---|---|---|
| Int8Array | 1 | 8 位有符号整数 |
| Uint8Array | 1 | 8 位无符号整型 |
| Int16Array | 2 | 16 位有符号整数 |
| Uint16Array | 2 | 16 位无符号整型 |
| Int32Array | 4 | 32 位有符号的整型 |
| Uint32Array | 4 | 32 位无符号的整型 |
| Float32Array | 4 | 32 位的浮点数 float |
| Float64Array | 8 | 64 位的浮点数 double |
gl.bindBuffer(target, buffer) - 绑定缓冲区。例如示例中的: gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
为什么要将绑定缓冲区对象?不能直接向缓冲区写入数据,只能通过目标写入数据。就像这样:

目标表示缓冲区的用途。例如这里的 gl.ARRAY_BUFFER 指包含顶点属性(例如顶点坐标、纹理坐标数据或顶点颜色数据)的缓冲区。
gl.bufferData(target, size, usage) - 将数据写入缓冲区。例如示例中的:gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
gl.STATIC_DRAW 是 usage 中的一种,指缓冲区的内容可能经常使用,而不会经常更改。内容被写入缓冲区,但不被读取。
此方法执行后,webgl 系统内部状态如下:

g.vertexAttribPointer(index, size, type, normalized, stride, offset) - 将整个缓冲区对象分配给 atttibute 变量。
例如示例中的:
// count - 指定缓冲区每个顶点的分量个数,缺少则按照 vertexAttrib3f 的补全方式
// gl.FLOAT 与上文的 Float32Array 对应
// normalized - 对于类型gl.FLOAT和gl.HALF_FLOAT,此参数无效
// stride - 指定相邻两个顶点间的字节数。默认为0
// offset - 指定缓冲区对象中的偏移量。即attribute 从缓冲区何处开始存储
gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0);
此方法执行后,webgl 系统内部状态如下:

疑惑: 说vertexAttribPointer最后一个参数(offset)必须是类型的字节长度的倍数。尝试将 0 改成 4,效果却是:

gl.enableVertexAttribArray(index) - 激活 attribute 变量,使缓冲区对 attribute 变量分配生效。
此方法执行后,webgl 系统内部状态如下:

gl.drawArrays(mode, first, count) - 执行顶点着色器,按照 mode 指定的参数绘制图形。first 指定从哪个点开始绘制,count 指绘制需要几个点。
能绘制的图形有:

Tip: drawArrays 更多细节请看这里
只要一次性将三个点绘制出来,其实三角形也就画出来了。
修改上面示例一行代码就好(gl.POINTS -> gl.LINE_LOOP):
// 前
gl.drawArrays(gl.POINTS, 0, vertices.vertexNumber);
// 后
gl.drawArrays(gl.LINE_LOOP, 0, vertices.vertexNumber);
效果如下:

效果如下:

核心代码如下:
// 定义四个点
const vertices = {
data: new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5,
0.5, -0.5,
]),
// 顶点数
vertexNumber: 4,
count: 2,
}
initVertexBuffers(gl, vertices)
// TRIANGLE_STRIP
gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertices.vertexNumber);
Tip:四个点的顺序有要求
效果如下:

在矩形的基础上,改为 gl.TRIANGLE_FAN 即可。
Tip:GL_TRIANGLE_FAN - 绘制各三角形形成一个扇形序列,以 v0 为起点:(v0, v1, v2)、(v0, v2, v3)、(v0, v3, v4)
其他章节请看:
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
基础版云数据库RDS的产品系列包括基础版、高可用版、集群版、三节点企业版,本文介绍基础版实例的相关信息。RDS基础版实例也称为单机版实例,只有单个数据库节点,计算与存储分离,性价比超高。说明RDS基础版实例只有一个数据库节点,没有备节点作为热备份,因此当该节点意外宕机或者执行重启实例、变更配置、版本升级等任务时,会出现较长时间的不可用。如果业务对数据库的可用性要求较高,不建议使用基础版实例,可选择其他系列(如高可用版),部分基础版实例也支持升级为高可用版。基础版与高可用版的对比拓扑图如下所示。优势 性能由于不提供备节点,主节点不会因为实时的数据库复制而产生额外的性能开销,因此基础版的性能相对于
我使用irb。下面是我写的代码。“斧头”..“bc”我期待"ax""ay""az""ba"bb""bc"但结果只是“斧头”..“bc”我该如何纠正?谢谢。 最佳答案 >puts("ax".."bc").to_aaxayazbabbbc 关于ruby-从结束值创建一系列字符串,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7617092/
使用RubyonRails,我使用给定的增量(例如每30分钟)用时间填充“选择”。目前我正在YAML文件中写出所有的可能性,但我觉得有一种更巧妙的方法。我想我想提供一个开始时间、一个结束时间、一个增量,并且目前只提供一个名为“关闭”的选项(想想“business_hours”)。所以,我的选择可能会显示:'Closed'5:00am5:30am6:00am...[allthewayto]...11:30pm谁能想出更好的方法,或者只是将它们全部“拼写”出来的最佳方法? 最佳答案 此答案基于@emh的答案。defcreate_hour
有道无术,术尚可求,有术无道,止于术。本系列SpringBoot版本3.0.4本系列SpringSecurity版本6.0.2本系列SpringAuthorizationServer版本1.0.2源码地址:https://gitee.com/pearl-organization/study-spring-security-demo文章目录前言1.OAuth2AuthorizationServerMetadataEndpointFilter2.OAuth2AuthorizationEndpointFilter3.OidcProviderConfigurationEndpointFilter4.N
我使用geokit和geokit-railsgemforrails有一段时间了,但我还没有找到答案的一个问题是如何找到一组点的计算聚合中心。我知道如何计算两点之间的距离,但不会超过2。我的理由是,我在同一个城市中有一系列的点……一切都完美的城市会有一个我可以使用的中心,但有些城市,比如柏林没有一个完美的中心。他们有多个中心,我只想使用我数据库中的所有地点列表来计算特定分布的中心。还有其他人遇到过这个问题吗?有什么建议吗?谢谢 最佳答案 之前从未使用过Geokit,这个操作背后的数学原理相对容易自己实现。假设这些点由纬度和经度组成,您
我有一个名为“FizzBuzz”的游戏的非常简单的Ruby实现(即给定输入数字,如果数字是3的倍数,则返回“Fizz”,如果是5的倍数,则返回“Buzz”,如果是多个,则返回“FizzBuzz”如果它不符合任何先前的条件,则两者和原始数字):classFizzBuzzdefanswer(number)multiple3=number%3==0multiple5=number%5==0returncasewhen(multiple3andmultiple5)then"FizzBuzz"whenmultiple3then"Fizz"whenmultiple5then"Buzz"el
一、概述在之前的一篇博文中,记录了AT24C01、AT24C02芯片的读写驱动,先将之前的相关文章include一下:1.IIC驱动:4位数码管显示模块TM1637芯片C语言驱动程序2.AT24C01/AT24C02读写:AT24C01/AT24C02系列EEPROM芯片单片机读写驱动程序本文记录分享AT24C04、AT24C08、AT24C16芯片的单片机C语言读写驱动程序。二、芯片对比介绍型号容量bit容量byte页数字节/页器件寻址位可寻址器件数WordAddress位数/字节数备注AT24C044k5123216A2A149/1WordAddress使用P0位AT24C088k1024