我有一个问题,可能类似于这个问题:WebGL shading/lighting too dark on some triangles
我试图将我的一个旧练习从使用已弃用 glaux 的 C++ OpenGL 移植到 WebGL。
问题是锥体显示错误,多切片时闪烁微弱:
有一个以阿基米德螺线为底的锥体,单切片时没问题:
左边它使用120个扇区,右边是12 个扇区,在 WebGL 上是可以的。
当我使用多个切片(例如 3 个切片)时,问题就开始了:
此图像看起来有点脏,请参见左侧图像。右侧图像中突出显示的上部三角形看起来像是显示了错误的照明。
当我显示法线时,它看起来像是正确计算的。因为法线在上下三角形的角上重合:
我想让代码片段更短,但是数学很长,也无法从 GitHub 中添加脚本,所以我将它们包含在内。表示顶点、法线和突出显示的法线。数学很简单,两个对称的阿基米德螺旋相互叠加:
螺旋被描述为 F(φ) = [φ * cos(φ), φ * sin(φ)]
计算正常 ∇F(φ) = [sin( φ) + φ * cos(φ), -cos(φ) + φ * sin(φ), φ]
verts
verts_norms
nverts
更新 1,着色器:
<canvas id = "cone1 heart geometry" width = "400" height = "400" class="yellow">
<script src="./webgl3/cone1_heart_geometry3.js"></script>
<script type="x-shader/x-vertex">
attribute vec4 coordinates;
attribute vec3 inputNormal;
uniform float isDrawNorms;
varying vec3 nm; //varying mediump
varying float isNormsColor; //in place of flag
void main()
{
gl_Position = coordinates;
nm = inputNormal;
isNormsColor = isDrawNorms;
}
</script>
<script type="x-shader/x-fragment">
precision mediump float;
varying vec3 nm; //varying mediump
varying float isNormsColor;
const vec4 greenColor = vec4( 0.0, 1.0, 0.0, 1.0);
const vec3 lightDirection = -normalize(vec3(-1.0, -1.0, -1.0));
void main()
{
if (isNormsColor < 0.5) gl_FragColor = vec4(greenColor.rgb * dot(lightDirection, normalize(nm)), 1.0);
if (isNormsColor > 0.5) gl_FragColor = vec4(0.5 * (nm + vec3(1.0)), 1.0);
}
</script>
</canvas>
<html>
<head>
<title>webgl test</title>
<style>
canvas.cyan {border:2px solid cyan;}
canvas.magenta {border:2px solid magenta;}
canvas.blue {border:2px solid blue;}
canvas.red {border:2px solid red;}
canvas.yellow {border:2px solid yellow;}
canvas.green {border:1px solid green;}
div.sample { display: inline-block;white-space: nowrap; border:1px solid magenta}
</style>
<script>
const RADGRAD = Math.PI / 180;
const C2PI = 2 * Math.PI;
function wrap(x)
{
if (isFinite(x)) return x;
if (x == Number.POSITIVE_INFINITY) return Number.MAX_VALUE;
if (x == Number.NEGATIVE_INFINITY) return Number.MIN_VALUE;
return 0.0;
}
function delta3v(p1, p2)
{
return {x:(p2.x - p1.x), y:(p2.y - p1.y), z:(p2.z - p1.z)};
}
function cross3v(p1, p2, p3)
{
let v1 = delta3v(p1, p2);
let v2 = delta3v(p1, p3);
return {x:(v1.y * v2.z - v1.z * v2.y), y:(v1.z * v2.x - v1.x * v2.z), z: (v1.x * v2.y - v1.y * v2.x)};
}
function mul3v(v, f)
{
return [v[0] * f[0], v[1] * f[1], v[2] * f[2]];
}
function dot3v(v, f)
{
return v[0] * f[0] + v[1] * f[1] + v[2] * f[2];
}
</script>
<script>
function buildGlProgram(canvasVar)
{
let canvas = null;
let gl = null;
if (typeof canvasVar == "string")
canvas = document.getElementById(canvasVar);
else if (typeof canvasVar == "object")
canvas = canvasVar;
//gl = canvas.getContext('webgl2');
//gl = canvas.getContext('experimental-webgl2');
gl = canvas.getContext('experimental-webgl');
gl.viewport(0, 0, canvas.width, canvas.height);
let canvas_id = canvas.id;
let codes = getGLShaderCodes (canvas);
let vertCode = codes.vertCode;
let vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);
let fragCode = codes.fragCode;
let fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, fragCode);
gl.compileShader(fragShader);
let shaderProgram = gl.createProgram();
gl.attachShader (shaderProgram, vertShader);
gl.attachShader (shaderProgram, fragShader);
gl.linkProgram (shaderProgram);
gl.useProgram (shaderProgram);
return {canvas:canvas,gl:gl, shaderProgram:shaderProgram};
}
function getGLShaderCodes (canvas)
{
let vertCode = "";
let fragCode = "";
let vertElement = null;
let fragElement = null;
let els = canvas.getElementsByTagName("script");
for (let i = 0; i < els.length; i++)
{
let el = els.item(i);
switch ( el.getAttribute("type") )
{
case "x-shader/x-vertex":
vertElement = el;
break;
case "x-shader/x-fragment":
fragElement = el;
break;
}
}
if(vertElement == null) vertElement = document.getElementById(canvas.getAttribute("id") + "_vertex_shader");
if(fragElement == null) fragElement = document.getElementById(canvas.getAttribute("id") + "_fragment_shader");
if(vertElement) vertCode = vertElement.innerText;
if(fragElement) fragCode = fragElement.innerText;
return {vertCode:vertCode, fragCode:fragCode};
}
</script>
</head>
<body>
<div class="sample">
<canvas id = "cone1 heart geometry" width = "400" height = "400" class="yellow">
<script>
{
let canvas = document.currentScript.parentElement;
function normalize3v(norm)
{
let len = Math.sqrt (norm[0] * norm[0] + norm[1] * norm[1] + norm[2] * norm[2]);
return [norm[0] / len, norm[1] / len, norm[2] / len];
}
function buildGeometry (sectors, revealInvisible)
{
let dfi = 2 * Math.PI / sectors;
let base = [], norms = [];
let sec2 = sectors / 2;
// Calculate from 0 to PI
for (let i = 0, fi = 0; i <= sec2; i++, fi += dfi)
{
//Calculate and normalize geometry: Resize all 0..PI to 0..1
base[i] = mul3v([ fi * Math.cos(fi), fi * Math.sin(fi), 0.0], [1.0 / Math.PI, 1.0 / Math.PI, 1.0 ]);
norms[i] = mul3v([Math.sin(fi) + fi * Math.cos(fi), -(Math.cos(fi) - fi * Math.sin(fi)), fi], [1.0 , 1.0 , 1.0 / Math.PI]);
norms[i] = normalize3v(norms[i]);
}
if(revealInvisible) base[0] = [0.5, 0.0, 0];
return {base:base, norms:norms};
}
function buildSides (geometry, slices, sectors)
{
let sec2 = sectors / 2;
let dh = 1 / slices;
let sides = [[], []];
for (let j = 0, s = 1, h = 1 - dh; j < slices; j++, s++, h -= dh)
{
sides[0][j] = [];
sides[1][j] = [];
for (let i = 0, i2 = sec2; i <= sec2; i++, i2--)
{
let bs = geometry.base[i];
let ns = geometry.norms[i];
sd = [s * bs[0] / slices, s * bs[1] / slices, h, ns[0], ns[1], ns[2]]; //[coords.xyz | norms.xyz]
sides[0][j][i] = [sd[0], sd[1], sd[2], sd[3], sd[4], sd[5]];
sides[1][j][i2] = [sd[0], -sd[1], sd[2], sd[3], -sd[4], sd[5]];
}
}
return sides;
}
function buildShape ()
{}
function buildCone(slices, sectors, revealInvisibles, snlen)
{
let nfi = 0;
if (sectors & 3) throw "Number of sectors must be a multiple of 4: " + sectors;
if (sectors < 8) throw "Must have no less than 8 sectors: " + sectors;
if (slices < 1) throw "Must have no less than 1 slices: " + slices;
let geo = buildGeometry (sectors, revealInvisibles);
let sides = buildSides(geo, slices, sectors);
let sec2 = sectors / 2;
let verts = [];
let verts_norms = [];
let nverts = [];
let i0 = 0, i1 = 1, i2 = 2;
let n0 = 0, n1 = 1, n2 = 2;
const nv = snlen;
for (let side of sides)
for (let j = 0; j < slices; j++)
for (let i = 0; i < sec2; i++)
{
let s11, s12; // s11 \ // s11 <- s12
let s21, s22; // s21 -> s22 // \ s22
if (j == 0) //tip of the cone
{
s11 = [0, 0, 1, 0, 0, 0]; //coord:xyz norm:000
s21 = side [j] [i]; s22 = side[j] [i+1];
}
else
{
s11 = side [j-1][i]; s12 = side[j-1][i+1];
s21 = side [j] [i]; s22 = side[j] [i+1];
}
//if (j == 0){
verts [i0] = s21[0];
verts [i1] = s21[1];
verts [i2] = s21[2];
verts_norms[i0] = s21[3];
verts_norms[i1] = s21[4];
verts_norms[i2] = s21[5];
nverts [n0] = s21[0];
nverts [n1] = s21[1];
nverts [n2] = -s21[2];
nverts [n0 + 3] = s21[0] + s21[3] / nv;
nverts [n1 + 3] = s21[1] + s21[4] / nv;
nverts [n2 + 3] = s21[2] - s21[5] / nv;
verts [i0 + 3] = s22[0];
verts [i1 + 3] = s22[1];
verts [i2 + 3] = s22[2];
verts_norms[i0 + 3] = s22[3];
verts_norms[i1 + 3] = s22[4];
verts_norms[i2 + 3] = s22[5];
nverts [n0 + 6] = s22[0];
nverts [n1 + 6] = s22[1];
nverts [n2 + 6] = -s22[2];
nverts [n0 + 9] = s22[0] + s22[3] / nv;
nverts [n1 + 9] = s22[1] + s22[4] / nv;
nverts [n2 + 9] = s22[2] - s22[5] / nv;
verts [i0 + 6] = s11[0];
verts [i1 + 6] = s11[1];
verts [i2 + 6] = s11[2];
verts_norms[i0 + 6] = s11[3];
verts_norms[i1 + 6] = s11[4];
verts_norms[i2 + 6] = s11[5];
nverts [n0 + 12] = s11[0];
nverts [n1 + 12] = s11[1];
nverts [n2 + 12] = -s11[2];
nverts [n0 + 15] = s11[0] + s11[3] / nv;
nverts [n1 + 15] = s11[1] + s11[4] / nv;
nverts [n2 + 15] = s11[2] - s11[5] / nv;
i0 += 9; i1 += 9; i2 += 9;
n0 += 18; n1 += 18; n2 += 18;
//}
// because this is the tip of the cone
if (j == 0) continue;
//if(0){
verts [i0] = s22[0];
verts [i1] = s22[1];
verts [i2] = s22[2];
verts_norms[i0] = s22[3];
verts_norms[i1] = s22[4];
verts_norms[i2] = s22[5];
nverts [n0] = s22[0];
nverts [n1] = s22[1];
nverts [n2] = -s22[2];
nverts [n0 + 3] = s22[0] + s22[3] / nv;
nverts [n1 + 3] = s22[1] + s22[4] / nv;
nverts [n2 + 3] = s22[2] - s22[5] / nv;
verts [i0 + 3] = s12[0];
verts [i1 + 3] = s12[1];
verts [i2 + 3] = s12[2];
verts_norms[i0 + 3] = s12[3];
verts_norms[i1 + 3] = s12[4];
verts_norms[i2 + 3] = s12[5];
nverts [n0 + 6] = s12[0];
nverts [n1 + 6] = s12[1];
nverts [n2 + 6] = -s12[2];
nverts [n0 + 9] = s12[0] + s12[3] / nv;
nverts [n1 + 9] = s12[1] + s12[4] / nv;
nverts [n2 + 9] = s12[2] - s12[5] / nv;
verts [i0 + 6] = s11[0];
verts [i1 + 6] = s11[1];
verts [i2 + 6] = s11[2];
verts_norms[i0 + 6] = s11[3];
verts_norms[i1 + 6] = s11[4];
verts_norms[i2 + 6] = s11[5];
nverts [n0 + 12] = s11[0];
nverts [n1 + 12] = s11[1];
nverts [n2 + 12] = -s11[2];
nverts [n0 + 15] = s11[0] + s11[3] / nv;
nverts [n1 + 15] = s11[1] + s11[4] / nv;
nverts [n2 + 15] = s11[2] - s11[5] / nv;
//}
i0 += 9; i1 += 9; i2 += 9;
n0 += 18; n1 += 18; n2 += 18;
}
return {verts:verts, norms:verts_norms, nverts:nverts};
}
let func = () =>
{
let prog = buildGlProgram(canvas);
let gl = prog.gl;
gl.clearColor(0.5, 0.5, 0.5, 0.9);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.clear (gl.COLOR_BUFFER_BIT);
////////////////////////////////////
const revealInvisibles = false;
let ns = 12, nh = 3, show_norms = true;
let obj;
try
{
obj = buildCone(nh, ns, revealInvisibles, 8);
}
catch(err)
{
alert(err);
}
let verts = obj.verts;
let norms = obj.norms;
////////////////////////////////////
let vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
let shaderProgram = prog.shaderProgram;
let coord = gl.getAttribLocation (shaderProgram, "coordinates");
gl.vertexAttribPointer (coord, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray (coord);
let normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(norms), gl.STATIC_DRAW);
let noord = gl.getAttribLocation (shaderProgram, "inputNormal");
gl.vertexAttribPointer (noord, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray (noord);
let drawNorms = gl.getUniformLocation(prog.shaderProgram, 'isDrawNorms');
gl.uniform1f(drawNorms, 0.0);
gl.drawArrays(gl.TRIANGLES, 0, ns * 3 + ns * 6 * (nh - 1));
if (show_norms)
{
gl.uniform1f(drawNorms, 1.0);
let nverts = obj.nverts;
let nvert_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, nvert_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(nverts), gl.STATIC_DRAW);
//coord = gl.getAttribLocation (shaderProgram, "coordinates");
gl.vertexAttribPointer (coord, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray (coord);
gl.drawArrays(gl.LINES, 0, 2* ( ns * 3 + ns * 6 * (nh - 1)));
//gl.drawArrays(gl.LINE_STRIP, 0, ns * 3 + ns * 6 * (nh - 1));
}
}
document.addEventListener('DOMContentLoaded', func);
}</script>
<script type="x-shader/x-vertex">
attribute vec4 coordinates;
attribute vec3 inputNormal;
uniform float isDrawNorms;
varying vec3 nm; //varying mediump
varying float isNormsColor; //in place of flag
void main()
{
gl_Position = coordinates;
nm = inputNormal;
isNormsColor = isDrawNorms;
}
</script>
<script type="x-shader/x-fragment">
precision mediump float;
varying vec3 nm; //varying mediump
varying float isNormsColor;
const vec4 greenColor = vec4( 0.0, 1.0, 0.0, 1.0);
const vec3 lightDirection = -normalize(vec3(-1.0, -1.0, -1.0));
void main()
{
if (isNormsColor < 0.5) gl_FragColor = vec4(greenColor.rgb * dot(lightDirection, normalize(nm)), 1.0);
if (isNormsColor > 0.5) gl_FragColor = vec4(0.5 * (nm + vec3(1.0)), 1.0);
}
</script>
</canvas>
</div>
</body>
</html>