서문에
[이전 노트](/articles/uniform 변수와 프래그먼트 셰이더-webgl 노트 4/) 에서, 우리는 비교적 어리석은 방식으로 복수 개의 점을 그렸지만, 효율이 낮았고, 삼각형과 같은 기본 도형을 그릴 수도 없었으며, 복잡한 도형은 더욱 논할 바가 없었습니다
一.buffer 의 이해
buffer 는 WebGL 시스템 내부에서 구획된 영역으로, 1 조의 정점 데이터를 저장할 수 있습니다. 예를 들어 정점 좌표 등. 정점 데이터를 buffer 에 쓴 후, 셰이더 프로그램 실행 시 buffer 에서 데이터를 읽을 수 있어, 데이터량이 클 때 특히 편리합니다
셰이더 프로그램 중에서는 직접 buffer 를 읽을 수는 없지만, 먼저 API 를 통해 셰이더 변수 값을 buffer 의 주소로 지정하고, 그 후 데이터 읽기 방식을 지정할 수 있습니다. 예를 들어 어디서 읽는가? 몇 개를 읽는가? 다음 정점의 데이터는 어디에 있는가? 간접적으로 buffer 에서 데이터를 읽습니다
二.타입화 배열
정점 데이터의 준비는 본래 따로 토론할 필요는 없었지만, 이 스텝은 매우 틀리기 쉽고, 올바른 코드는 다음과 같습니다:
// 一次性传递一组顶点数据
//!!! 注意:不是一般数组,需要 new 类型化数组,否则报错
// GL ERROR :GL_INVALID_OPERATION :glDrawArrays: attempt to access out of range vertices in attribute 0
// var arrVtx = [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 0.0, 0.0];
var arrVtx = new Float32Array([-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 0.0, 0.0]);
특히 주의:buffer 에 쓸 준비를 하는 데이터는 일반적인 배열이 아니며, 타입화 배열을 new 할 필요가 있습니다. 否则 에러가 발생하며, 에러 정보는 다음과 같습니다:
GL ERROR :GL_INVALID_OPERATION :glDrawArrays: attempt to access out of range vertices in attribute 0
에러 정보만으로부터 정점 데이터에 거슬러 올라가기 어렵기 때문에, 특히 주의가 필요합니다
GLSL ES 는 강형 언어이고, js 는 약형입니다. js 배열 요소에는 제한이 없고, GLSL ES 는 매우 엄격한 데이터를 필요로 하기 때문에, 타입화 배열이 태어났습니다. WebGL 에는, 타입화 배열이 이하 8 종류 있습니다:
-
Int8Array
-
UInt8Array
-
Int16Array
-
UInt16Array
-
Int32Array
-
UInt32Array
-
Float32Array
-
Float64Array
차이는 각 요소가 차지하는 바이트 수가 다른 것입니다 (타입화 배열에는 BYTES_PER_ELEMENT 속성이 있어, 배열 내의 각 요소가 차지하는 바이트 수를 취득할 수 있습니다. 예를 들어 Float32Array 는 각 요소가 4 바이트를 차지합니다). 일반적인 배열과 비교하여, 타입화 배열은 push 와 pop 메서드를서포트하지 않지만, 「대량의 요소가 모두 같은 형」에 대해 최적화되어 있습니다
三.buffer 의 사용
buffer 의 작성에서 1 조의 정점 데이터의 쓰기까지, 5 가지의 일을 할 필요가 있습니다
###1.buffer 의 작성
직접 API 를 호출하여 buffer 를 작성합니다. 다음과 같습니다:
// 1.创建 buffer
var vBuffer = gl.createBuffer();
if (!vBuffer) {
console.log('Failed to create buffer');
return;
}
작성에 실패하면 null 를 반환하지만, 이 종류의 함수의 에러율은 비교적 낮기 때문에, 여기서는 엄격한 에러 판단을 하지 않습니다 (에러율이 높은 부분은 반드시 엄격한 판단을 할 필요가 있습니다. 예를 들어 셰이더 소스 프로그램의 컴파일)
###2.버퍼 영역 오브젝트를 타겟에 바인드
버퍼 영역을 작성한 후, 버퍼 영역 오브젝트를 WebGL 시스템 중에 이미 존재하는 target 에 바인드할 필요가 있습니다
// 2.把缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
buffer 는 이미 작성되었지만, 다음 스텝은 데이터를 쓸べき 아닙니까? 타겟을 바인드하여 무엇을 하는 것일까요?
우리가 직접 버퍼 영역에 데이터를 쓸 수 없기 때문에, target 에 쓸 수밖에 없기 때문입니다. 따라서 버퍼 영역에 데이터를 쓰려면, 먼저 target 과 buffer 를 바인드�� 필요가 있습니다. target 은 버퍼 영역 오브젝트의 용도를 나타내며, 값은 gl.ARRAY_BUFFER 또는 gl.ELEMENT_ARRAY_BUFFER 입니다. 전자는 버퍼 영역 오브젝트가 정점 데이터를 포함하고 있는 것을 나타내고, 후자는 정점의 인덱스 값을 포함하고 있는 것을 나타냅니다
###3.버퍼 영역 오브젝트에 데이터를 쓴다
gl.bufferData(target, data, usage) 를 통해 데이터를 씁니다
// 3.向缓冲区对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, arrVtx, gl.STATIC_DRAW);
그 중에서 data 는타입화 배열로, 앞에서 이미 소개했습니다
usage 는 버퍼 영역 데이터의 용도를 나타내며, WebGL 은 용도에 따라 최적화를 수행합니다. 값은 gl.STATIC_DRAW(버퍼 영역 오브젝트에 1 회만 데이터를 쓰지만, 여러 번 그릴 필요가 있는), gl.STREAM_DRAW(버퍼 영역 오브젝트에 1 회만 데이터를 쓰고, 그 후 수회 그린다) 또는 gl.DYNAMIC_DRAW(버퍼 영역 오브젝트에 여러 번 데이터를 쓰고, 여러 번 그린다) 입니다. 차이는 그다지 명확하지 않고, moreover퍼포먼스에 영향을 줄 가능성만있으며, 최종 결과에는 영향을 주지 않습니다
###4.버퍼 영역 오브젝트를 a_Position 변수에 할당
buffer 의 준비가 되었습니다. 이제는 셰이더 변수에 값을 할당하고, 셰이더에 이러한 데이터를 나중에 어떻게 사용하는지 알릴 필요가 있습니다
// 4.将缓冲区对象分配给 a_Position 变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 等价于
// gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, arrVtx.BYTES_PER_ELEMENT * 2, 0);
gl.vertexAttribPointer(location, size, type, normalized, stride, offset) 를 통해 attribute 오브젝트에 버퍼 포인터를 할당합니다. 파라미터의 의미는 다음과 같습니다:
gl.vertexAttribPointer(location, size, type, normalized, stride, offset)
---
size 버퍼 중 각 정점의 성분수 (1~4), 각 정점 데이터 중 셰이더 변수에 할당하는 성분수를 나타냄
type 데이터 형식, 값은 gl.UNSIGNED_BYTE, gl.SHORT, gl.UNSIGNED_SHORT, gl.INT, gl.UNSIGNED_INT, gl.FLOAT 의 5 종
normalized true|false, 비부동 소수점형 데이터를 [0, 1] 또는 [-1, 1] 구간에 정규화하는지 여부를 나타냄
stride 인접하는 2 개의 정점 간의 바이트 수를 지정, 디폴트는 0
offset 버퍼 영역 오브젝트 중의 오프셋량, 버퍼의 offset 위치에서 쓰기 시작, 처음부터라면 0
특히 주의:stride 파라미터는 비교적 특수로, 각 정점이 n 개의 데이터를 가지는 경우, stride 를 arr.BYTES_PER_ELEMENT * n 으로 설정하는 효과는 0 과 같습니다. 현재의 정점 데이터 읽기 종료 후, stride 가 0 이라면, 현재의 정점 데이터종료의 위치에서 다음 정점의 데이터를 읽기 시작하고, stride 가 0 이 아니라면, 현재의 정점 데이터개시의 위치에서 stride 가 지정하는 바이트 수를 스킵하고 나서 읽기를 시작합니다
P.S.이해하기 어려울지도 모르지만, 사실은 이렇습니다. 이후의 구체 예에서 더욱 설명합니다
###5.a_Position 변수와それに 할당된 버퍼 영역 오브젝트를 접속
buffer 의 준비가 되고, 셰이더에 데이터의 사용 방법도 알렸습니다. 마지막에 변수와 버퍼 영역을 접속할 필요가 있습니다
//!!! 注意:分配完还要 enable 连接
// 5.连接 a_Position 变量和分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);
이 스텝은 매우잊기 쉽고, 논리적으로는 이미 완전하며, 이 스텝은 순수하게 여분이지만, API 는 이렇고, 이 스텝은 아래 그림의 스위치와 같은 것입니다:
[caption id="attachment_901" align="alignnone" width="893"]
webgl-buffer[/caption]
###6.복수 개의 점을 그린다
// 绘制点
gl.drawArrays(gl.POINTS, 0, arrVtx.length / 2);
드디어 3 번째 파라미터를 사용했습니다. 더 이상 draw a point 가 아니며, 3 번째 파라미터는 그릴 정점의 개수를 나타냅니다. 본례 중 배열 길이 arrVtx.length 를 각 정점의 데이터 수 2 로 나누면, arrVtx 중의 모든 정점 (합계 5 개) 을 그리는 것을 나타냅니다
四.DEMO
위의 코드를 포함한 완전한 예는, 이하를 참조:http://www.ayqy.net/temp/webgl/buffer/index.html
五.정리
본례에서는, buffer 를 통해 5 개의 점의 좌표 (4 개의 모서리 및 중심) 를 전송하고, 한번에 그렸습니다. 가장 기본적인 입력 데이터의 문제를 해결했습니다
buffer 를 언락한 후, 도원 및 다소 복잡한 도형에 만질 수 있습니다. 다음 노트에서는 가장 신비한 도원——삼각형을 그립니다. 이렇게 삼각형을 칭찬하는 것은 정말로 틀리지 않습니다. 사실상, 삼각대로 전 세계를拼이 낼 수 있습니다. 인물 모델, BOSS 모델, 화초 수목 등 모두 삼각대로拼이 낸 것입니다
참고 자료
- 《WebGL 프로그래밍 가이드》
아직 댓글이 없습니다