[英]Drawing parametric shapes in webGL (without three.js)
我編寫了一個程序,僅使用HTML5和常規Javascript即可繪制一些參數化形狀(球形,圓環形和圓柱形)。 我正在嘗試轉換此代碼,以便它使用WebGL,並按照本教程中的說明用三角帶實現形狀。 我的困惑點是,甚至根本沒有使用三角帶來創建球體。 我之前所做的方式只是基於計算for循環中的緯度線並在該循環內嵌套來計算每個水平切片或圓的繪制位置,而該點是該圓上每個點的計算。 生成所有這些點之后,我將它們傳遞給一個函數,該函數將所有頂點添加到數組中,該數組將返回並傳遞給曲線繪制函數,該函數使用moveTo()
和lineTo()
以便在每個點之間繪制線。 問題是我不知道使用三角形形狀時webGL中的moveTo()
和lineTo()
等效。 如何將實現轉換為WebGL?
這是我原始實現中的一些代碼:
//Calculates point on sphere
function spherePoint(uv) {
var u = uv[0];
var v = uv[1];
var phi = -Math.PI/2 + Math.PI * v;
var theta = 2 * Math.PI * u;
return [ Math.cos(phi) * Math.cos(theta),
Math.cos(phi) * Math.sin(theta),
Math.sin(phi)];
}
// Takes the parametric function as an argument and constructs 3D shape
function makeShape(num_u, num_v, eq, possRad) {
var shell = [];
for (var j = 0 ; j <= num_v ; j++) {
var v = j / num_v;
shell.push([]);
for (var i = 0 ; i <= num_u ; i++) {
var u = i / num_u;
var p = eq([u, v], possRad);
shell[j].push(p);
}
}
return shell;
}
// Used to create shapes to render parametric surface
function renderShape(shape) {
var num_j = shape.length;
var num_i = shape[0].length;
for (var j = 0 ; j < num_j - 1 ; j++)
for (var i = 0 ; i < num_i - 1 ; i++) {
plotCurve([shape[j][i],
shape[j + 1][i],
shape[j + 1][i + 1],
shape[j][i + 1]]);
}
}
//plot curve on canvas
function plotCurve(C) {
g.beginPath();
for (var i = 0 ; i < C.length ; i++)
if (i == 0)
moveTo(C[i]);
else
lineTo(C[i]);
g.stroke();
}
function moveTo(p) {
var q = m.transform(p); // APPLY 3D MATRIX TRANFORMATION
var xy = viewport(q); // APPLY VIEWPORT TRANSFORM
g.moveTo(xy[0], xy[1]);
}
function lineTo(p) {
var q = m.transform(p); // APPLY 3D MATRIX TRANFORMATION
var xy = viewport(q); // APPLY VIEWPORT TRANSFORM
g.lineTo(xy[0], xy[1]);
}
這是一個非常基本的WebGL問題。 有關webgl的更多教程可能會有所幫助 。
WebGL僅繪制數據塊。 它實際上沒有lineTo
或moveTo
。 取而代之的是給它提供數據緩沖區,告訴它如何從這些緩沖區中提取數據,然后編寫一個函數(頂點着色器)以使用該數據告訴WebGL如何將其轉換為裁剪空間坐標以及是否繪制點,線,或帶有結果的三角形。 您還提供了一個函數(片段着色器)來告訴它要用於點,線或三角形的顏色。
基本上,要繪制要繪制的對象,需要為該球體上的每個矩形生成2個三角形。 換句話說,您需要為每個矩形生成6個頂點。 原因是為了以不同的顏色繪制每個三角形,您不能共享任何頂點,因為顏色與頂點相關聯。
因此,對於一個矩形,您需要生成這些點
0--1 4
| / /|
|/ / |
2 3--5
其中0、1和2是粉紅色的點,而3、4、5是綠色的點。 1和4具有相同的位置,但是由於它們的顏色不同,因此它們必須是不同的點。 與第2點和第3點相同。
var pink = [1, 0.5, 0.5, 1];
var green = [0.5, 1, 0.5, 1];
var positions = [];
var colors = [];
var across = 20;
var down = 10;
function addPoint(x, y, color) {
var u = x / across;
var v = y / down;
var radius = Math.sin(v * Math.PI);
var angle = u * Math.PI * 2;
var nx = Math.cos(angle);
var ny = Math.cos(v * Math.PI);
var nz = Math.sin(angle);
positions.push(
nx * radius, // x
ny, // y
nz * radius); // z
colors.push(color[0], color[1], color[2], color[3]);
}
for (var y = 0; y < down; ++y) {
for (var x = 0; x < across; ++x) {
// for each rect we need 6 points
addPoint(x , y , pink);
addPoint(x + 1, y , pink);
addPoint(x , y + 1, pink);
addPoint(x , y + 1, green);
addPoint(x + 1, y , green);
addPoint(x + 1, y + 1, green);
}
}
這是上面渲染的球體,但沒有任何照明,透視圖或任何東西。
var pink = [1, 0.5, 0.5, 1]; var green = [0.5, 1, 0.5, 1]; var positions = []; var colors = []; var across = 20; var down = 10; function addPoint(x, y, color) { var u = x / across; var v = y / down; var radius = Math.sin(v * Math.PI); var angle = u * Math.PI * 2; var nx = Math.cos(angle); var ny = Math.cos(v * Math.PI); var nz = Math.sin(angle); positions.push( nx * radius, // x ny, // y nz * radius); // z colors.push(color[0], color[1], color[2], color[3]); } for (var y = 0; y < down; ++y) { for (var x = 0; x < across; ++x) { // for each rect we need 6 points addPoint(x , y , pink); addPoint(x + 1, y , pink); addPoint(x , y + 1, pink); addPoint(x , y + 1, green); addPoint(x + 1, y , green); addPoint(x + 1, y + 1, green); } } var gl = twgl.getWebGLContext(document.getElementById("c")); var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]); var arrays = { position: positions, color: colors, }; var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays); var uniforms = { resolution: [gl.canvas.width, gl.canvas.height], }; gl.useProgram(programInfo.program); twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); twgl.setUniforms(programInfo, uniforms); twgl.drawBufferInfo(gl, bufferInfo);
canvas { border: 1px solid black; }
<canvas id="c"></canvas> <script id="vs" type="not-js"> attribute vec4 position; attribute vec4 color; uniform vec2 resolution; varying vec4 v_color; void main() { gl_Position = position * vec4(resolution.y / resolution.x, 1, 1, 1); v_color = color; } </script> <script id="fs" type="not-js"> precision mediump float; varying vec4 v_color; void main() { gl_FragColor = v_color; } </script> <script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
如果以后要點亮它,則還需要法線 (可用於確定物體朝向哪個方向的值)。 我們可以通過添加
var normals = [];
在addPoint
function addPoint(x, y, color) {
var u = x / across;
var v = y / down;
var radius = Math.sin(v * Math.PI);
var angle = u * Math.PI * 2;
var nx = Math.cos(angle);
var ny = Math.cos(v * Math.PI);
var nz = Math.sin(angle);
positions.push(
nx * radius, // x
ny, // y
nz * radius); // z
colors.push(color[0], color[1], color[2], color[3]);
normals.push(nx, ny, nz);
}
這是燈光被黑的示例
var pink = [1, 0.5, 0.5, 1]; var green = [0.5, 1, 0.5, 1]; var positions = []; var colors = []; var normals = []; var across = 20; var down = 10; function addPoint(x, y, color) { var u = x / across; var v = y / down; var radius = Math.sin(v * Math.PI); var angle = u * Math.PI * 2; var nx = Math.cos(angle); var ny = Math.cos(v * Math.PI); var nz = Math.sin(angle); positions.push( nx * radius, // x ny, // y nz * radius); // z normals.push(nx, ny, nz); colors.push(color[0], color[1], color[2], color[3]); } for (var y = 0; y < down; ++y) { for (var x = 0; x < across; ++x) { // for each rect we need 6 points addPoint(x , y , pink); addPoint(x + 1, y , pink); addPoint(x , y + 1, pink); addPoint(x , y + 1, green); addPoint(x + 1, y , green); addPoint(x + 1, y + 1, green); } } var gl = document.getElementById("c").getContext("webgl"); var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]); var arrays = { position: positions, normal: normals, color: colors, }; var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays); var uniforms = { resolution: [gl.canvas.width, gl.canvas.height], lightDirection: [0.5, 0.5, -1], }; gl.useProgram(programInfo.program); twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); twgl.setUniforms(programInfo, uniforms); twgl.drawBufferInfo(gl, bufferInfo);
canvas { border: 1px solid black; }
<canvas id="c"></canvas> <script id="vs" type="not-js"> attribute vec4 position; attribute vec4 color; attribute vec3 normal; uniform vec2 resolution; varying vec4 v_color; varying vec3 v_normal; void main() { gl_Position = position * vec4(resolution.y / resolution.x, 1, 1, 1); v_color = color; v_normal = normal; } </script> <script id="fs" type="not-js"> precision mediump float; varying vec4 v_color; varying vec3 v_normal; uniform vec3 lightDirection; void main() { float light = pow(abs(dot(v_normal, normalize(lightDirection))), 2.0); gl_FragColor = vec4(v_color.xyz * light, v_color.a); } </script> <script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
PS:您發布的圖片實際上是每個矩形繪制更多的三角形。 綠色和粉紅色之間的分界不是直的。
gl.LINES可以很好地繪制連接的線。 因此,lineTo(x,y,z)只會再添加一個頂點到用於存儲行數據的VBO中。 moveTo(x,y,z)只是在兩行之間創建一個“中斷”。 這可以通過新的drawArrays調用來完成。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.