서문에
[이전 노트](/articles/ 用 webgl 绘制一个矩形-webgl 笔记 1/) 에서 정점 셰이더 소스 프로그램 내에서 gl_Position 을 하드코딩했습니다. 점의 위치를 수정하고 싶으면 셰이더 소스 프로그램을 수정해야 하며, 이는 물론 의미가 없습니다. 우리는 더욱 유연한 방식이 필요합니다. 예를 들어 attribute 변수입니다
一.util 의 캡슐화
[이전 노트](/articles/ 用 webgl 绘制一个矩形-webgl 笔记 1/) 에서 의사 사각형 (조금 큰 점) 을 그리기 위해 70 행 이상의 코드를 썼지만, 오류 체크만 으로, 호환성 처리 는 아직 하지 않았습니다. 모든 스텝에서 WebGL 네이티브 API 를 호출하고 있으며, 플로우는 매우 명확하지만, 실제로는 너무 번거롭습니다. 1 개의 점에 70 행, 2 개의 점에 140 행은 안 되므로, 먼저 util 을 캡슐화합니다
시작하기 전에, 몇 가지 문제에 주의할 필요가 있습니다:
###1.getContext 의 호환성 문제
getContext('webgl') 는 표준 방식이지만, 저버전 브라우저에서는 다른 문자열을 전달해야 합니다. 다음과 같습니다:
/**
* Creates a webgl context.
* @param {!Canvas} canvas The canvas tag to get context
* from. If one is not passed in one will be created.
* @return {!WebGLContext} The created context.
*/
var create3DContext = function(canvas, opt_attribs) {
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
var context = null;
for (var ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii], opt_attribs);
} catch(e) {}
if (context) {
break;
}
}
return context;
}
위의 코드는 google 팀의 webgl-utils.js 에서 발췌한 것으로, create3DContext 외에 requestAnimationFrame 의 호환 버전도 제공하며, 나중에 애니메이션에서도 사용하므로, 우리의 util 은 직접 webgl-utils.js 를 include 합니다
###2.program 오브젝트의 참조를 유지
gl.createProgram() 이 반환하는 program 오브젝트는 셰이더의 초기화 프로세스 중에서 사용될 뿐만 아니라,これから行う attribute 변수의 값 전달에서도 사용합니다. program 오브젝트는 js 와 셰이더 내부 통신의 가교이지만, 문제는WebGL 이 gl.getProgram() 와 같은 getter 를 제공하지 않기 때문에, util 을 캡슐화할 때 반드시 program 오브젝트의 참조를 유지하고, 외부에서 사용할 수 있도록 해야 합니다. 예를 들어:
function initShaders(vsSrc, fsSrc) {
var program = _createProgram(vsSrc, fsSrc);
if (!program) {
log('Failed to create program');
return false;
}
gl.useProgram(program);
// Save program object
//!!! because there is no getter like gl.getProgram()
glUtil.program = program;
return true;
}
위의 코드는 필자의 gl-util.js 에서 발췌한 것으로, 필요하면 직접 사용할 수 있습니다. 인터페이스 설명은 다음과 같습니다:
// 全局变量:glUtil
return {
program: null, // runtime assignment
getContext: getContext,
initShaders: initShaders,
debug: function(onOrOff) {
if (typeof onOrOff === 'boolean') {
debug = onOrOff;
}
}
};
二.용어 및 API 설명
###1.셰이더
셰이더는 정점 셰이더와 프래그먼트 셰이더로 나뉩니다:
- 정점 셰이더
정점의 특성 (위치, 색 등) 을 기술하는 프로그램. 정점이란 이차원 또는 삼차원 공간 중의 1 개의 점을 지칭하며, 예를 들어 이차원 또는 삼차원 도형의 단점 또는 교점
- 프래그먼트 셰이더
逐 프래그먼트 처리 프로세스 (조명 등) 를 수행하는 프로그램. 프래그먼트는 WebGL 용어로, 간단히 말해 픽셀 (이미지의 유닛) 이라고 이해할 수 있습니다
P.S. 실제로 프래그먼트는 화면에 표시되는 1 개의 픽셀을 나타내며, 그 픽셀 점의 위치, 색 및 다른 정보도 포함합니다
###2.정점 셰이더 소스 프로그램
GLSL ES 로 쓰인 프로그램으로, 문자열 형식으로 WebGL 시스템 내부에 전달됩니다
// 顶点着色器源程序
var vsSrc = 'void main() {' +
'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 设置坐标
'gl_PointSize = 200.0;' + // 设置尺寸
'}';
그 중에서, gl_Position 에 값을 할당하는 것은 필수로,否则셰이더는 정상적으로 작동하지 않습니다. gl_Position 은 vec4 타입으로, 즉 사차원 벡터 (동차 좌표, 마지막 성분은 보통 1.0 을 취함)
gl_PointSize 에는 디폴트 값 1.0 이 있어, 값을 할당할 필요는 없습니다. gl_PointSize 는 float 타입으로, 받아들이지 않습니다3f(C 언어 중에서 3f 는 부동 소수점수 3 을 나타냄) 와 같은 형식으로, moreover고립점을 그릴 때만 유효합니다 (고립점의 그리기란 무엇인가, 나중에 설명합니다)
###3.프래그먼트 셰이더 소스 프로그램
정점 셰이더 소스 프로그램과 같지만, 제어하는 것은 프래그먼트 셰이더입니다
// 片元着色器源程序
var fsSrc = 'void main() {' +
'gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);' + // 设置颜色
'}';
gl_FragColor 는유일의 내장 변수로, vec4 타입이지만, rgba 색 값을 나타냅니다. rgba 의取值는 모두 0~1 의로, CSS 중의 rgb 가 0~255 의와는 다릅니다
###4.그리기 조작
그리기 조작에는 1 개의 인터페이스만 있지만, 기능은 매우 강력합니다:
// 绘制矩形(一个点,但点的尺寸略大)
gl.drawArrays(gl.POINTS, 0, 1);
API 의 구체 정보는 다음과 같습니다:
gl.drawArrays(mode, first, count)
mode 指定绘制方式,接受常量:
---
gl.POINTS 孤立点(v0, v1...)
gl.LINES 孤立线段(v0v1, v2v3...)
gl.LINE_STRIP 连续线段(v0v1, v1v2...)
gl.LINE_LOOP 连续线段连成圈(v0v1, v1v2...vnv0)
gl.TRIANGLES 孤立三角形(v0v1v2, v3v4v5...)
gl.TRIANGLE_STRIP 连续三角带(v0v1v2, v2v1v3, v2v3v4...),用来构造复杂模型,如球、树
注意:第二个三角形是 v2v1v3,不是 v1v2v3,这是为了保证第二个三角形的绘制也按照逆时针顺序
gl.TRIANGLE_FAN 三角扇(v0v1v2, v0v2v3, v0v3v4),也可以用来构造复杂模型,但在实际应用中不常见,因为需要求出所有三角形的公共顶点 v0
first 指定从哪个顶点开始绘制
---
0 表示从第一个开始,配合缓冲区对象使用(缓冲区对象中可以存放多个顶点信息)
count 指定需要绘制点的个数,同样配合缓冲区对象使用
---
调用 gl.drawArray 时,顶点着色器会被执行 count 次,每次处理一个顶点,顶点着色器执行完毕后片元着色器开始执行,给片元涂色(中间还有光栅化的过程)
특히 주의:정점 셰이더는逐 정점실행되고, 프래그먼트 셰이더는逐 프래그먼트실행되며, 1 회만 실행되는 것은 아닙니다
三.attribute 변수
셰이더 소스 프로그램 중에서 변수를 선언하고, 그 후 js 가 이 변수에 값을 전달하고, 더욱이 그리기함으로써, 점의 위치를 동적으로 수정할 수 있습니다. 간단히 말해 이렇습니다
###1.attribute 변수의 선언
// 顶点着色器源程序
var vsSrc = 'attribute vec4 a_Position;' +
'void main() {' +
'gl_Position = a_Position;' + // 设置坐标
'gl_PointSize = 200.0;' + // 设置尺寸
'}';
// 片元着色器源程序
//...不变
C 언어의 변수 선언 방식에 유사하며, attribute 는 저장 한정자라고 불리고, vec4 는 변수 타입입니다. 변수명 a_Position 중의 a_ 프리픽스는 변수가 attribute 변수임을 나타내며, 물론 이는 습관이지만, 채택하는 것을 권장합니다
P.S.왜 정점 셰이더 소스 프로그램 중에서 attribute 변수를 선언하는지, 프래그먼트...가 아닌지. 우리가 점의 좌표를 동적으로 수정하고 싶기 때문이며, 좌표 정보는 정점 셰이더 소스 프로그램 중의 gl_Position 에서 오고, 프래그먼트 셰이더와는 관계가 없기 때문입니다
주의:정점 셰이더만이 attribute 변수를 사용할 수 있고, 프래그먼트 셰이더에서는 uniform 변수를 사용할 필요가 있습니다. 더욱이 varying 변수도 있으며, 구체의 차이는 GLSL ES 노트에서 더욱 상세히 설명합니다
###2.셰이더의 초기화
직접 util 을 호출하고, 1 행의 코드로 나머지 7 개의 일 (createShader 에서 useProgram 까지의 7 개의 스텝) 을 완료할 수 있습니다
// 1.初始化着色器
glUtil.initShaders(vsSrc, fsSrc);
###3.attribute 변수에 값을 할당
변수에의 값 할당은 2 스텝으로 나뉘며, 먼저 저장 위치를 취득하고, 그 후 값을 할당합니다
// 2.给 attribute 变量赋值
// 获取 attribute 变量的存储位置
var a_Position = gl.getAttribLocation(glUtil.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// 把顶点位置传递给 attribute 变量
gl.vertexAttrib3f(a_Position, 0.5, 0.0, 0.0);
###4.그리기
// 绘制点
gl.drawArrays(gl.POINTS, 0, 1);
첫 번째 파라미터에 gl.POINTS 를 전달하면 고립점의 그리기를 나타내며, 선분, 삼각형 등도 그릴 수 있습니다. 나중에 설명합니다
四.DEMO
위의 코드를 포함한 완전한 예는, 이하를 참조:http://www.ayqy.net/temp/webgl/attribute/index.html
五.정리
현재 우리는 정점 좌표를 동적으로 수정할 수 있지만, 물론, 이는 매우 간단한 스텝으로, 뒤에는 더욱:
- 마우스 클릭 위치에서 점을 그리려면?
답은 상상에那么简单하지 않으며, canvas 와 WebGL 좌표계가 달라, 좌표를 변환할 필요가 있습니다
- 점의 색을 수정하려면?
uniform 변수로 충분합니다
- 복수의 점을 그리려면
gl.drawArrays(gl.POINTS, 0, 1); 를 루프?할 수 있습니까?
이들은 뒤의 노트에서 토론할 문제로, 먼저 조급해하지 마십시오
참고 자료
- 《WebGL 프로그래밍 가이드》
아직 댓글이 없습니다