我正试图用WebGL在javascript中建模一个地球仪,并通过遵循本教程开始这样做。
在网格上显示的纹理应该具有最小的失真。为了避免扭曲的面积,距离和形状,我使用中断的正弦波地图投影而不是等长线投影。我使用在本站上找到的地球纹理进行测试。
我面临的问题是顶点呈现在期望的纹理边界之外。越高的I曲柄上升顶点数量越少“外部”纹理被看到(从左到右,纬度和经度带计数每一步加倍):

投影公式应该是正确的,我能够生成一个模板文件以确保-在c# tho中,我不太习惯js:

所以,我的问题是,如何改进球面的近似,避免显示在预定纹理区域之外的像素?
相关代码:
// the projection used in the tutorial
function project_equirectangular(xangle, yangle) {
return [xangle, yangle];
}
// the projection I intend to use to minimize distortion
function project_sinusoidial(xangle, yangle, segments) {
var segment = Math.round(segments*yangle-1/2)+1;
var segment_middle = (segment-1/2)/segments;
return [
(yangle-segment_middle)*Math.cos(Math.PI*(1/2-xangle))+segment_middle,
xangle
];
}
// creating the spherical mesh for the globe
function initBuffers() {
var M = 6;
var N = 2*M;
var radius = 1;
var vertexPositionData = [];
var normalData = [];
var textureCoordData = [];
for (var m=0; m <= M; m++) {
var theta = Math.PI * m / M;
var sinTheta = Math.sin(theta);
var cosTheta = Math.cos(theta);
for (var n=0; n <= N; n++) {
var phi = 2 * Math.PI * n / N;
var sinPhi = Math.sin(phi);
var cosPhi = Math.cos(phi);
var x = cosPhi * sinTheta;
var y = cosTheta;
var z = sinPhi * sinTheta;
var proj = project_sinusoidial(m/M, n/N, 12);
var u = 1 - proj[0];
var v = 1 - proj[1];
normalData.push(x);
normalData.push(y);
normalData.push(z);
textureCoordData.push(u);
textureCoordData.push(v);
vertexPositionData.push(radius * x);
vertexPositionData.push(radius * y);
vertexPositionData.push(radius * z);
}
}
var indexData = [];
for (var m=0; m < M; m++) {
for (var n=0; n < N; n++) {
var first = (m * (N + 1)) + n;
var second = first + N + 1;
indexData.push(first);
indexData.push(second);
indexData.push(first + 1);
indexData.push(second);
indexData.push(second + 1);
indexData.push(first + 1);
}
}
planetVertexNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, planetVertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normalData), gl.STATIC_DRAW);
planetVertexNormalBuffer.itemSize = 3;
planetVertexNormalBuffer.numItems = normalData.length / 3;
planetVertexTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, planetVertexTextureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordData), gl.STATIC_DRAW);
planetVertexTextureCoordBuffer.itemSize = 2;
planetVertexTextureCoordBuffer.numItems = textureCoordData.length / 2;
planetVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, planetVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexPositionData), gl.STATIC_DRAW);
planetVertexPositionBuffer.itemSize = 3;
planetVertexPositionBuffer.numItems = vertexPositionData.length / 3;
planetVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, planetVertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), gl.STATIC_DRAW);
planetVertexIndexBuffer.itemSize = 1;
planetVertexIndexBuffer.numItems = indexData.length;
}
发布于 2018-05-12 15:55:36
你的纹理坐标在你的球体上并不是连续的。它们之间有很大的不连续性。因此,您不能使用通常的机制生成您的球体;您必须根据您想要的纹理坐标来生成它。也就是说,你的网格几何必须与你的纹理的不连续性相匹配。
你的纹理中有这些数据条。因此,这些条必须是你的顶点生成的基础,包括位置。所以,对于你来说,一个球不是一个“球”,而是形成一个球形形状的这些抛物面带的序列。每个单独的条必须与其他条分离;它们不能重用顶点。
同时,您必须确保每个条带的边缘生成与其相邻条带相同的位置值。否则,你可以得到之间的空白条。
另一件事,你需要做的是确保你的纹理有适当的数据在边缘的条带。边缘三角形之间的纹理坐标插值,以及纹理过滤,有时会访问纹理的“白色”区域中的值。所以你需要在条带的边缘有一个像素的附加信息,这样过滤就不会吸引不需要的数据。这必须在每一个mipmap级别上完成。
通常的想法是简单地重复相邻的纹理。
然而,广义地说,如果您想要纹理(而且您有能力生成任何您想要的全球纹理),请使用cubemap。这种方法会产生最少的失真,并且所需的努力最少(除了获取立方体纹理本身)。此外,您甚至不需要纹理坐标;只需使用插值的法线作为纹理坐标。
https://stackoverflow.com/questions/50307742
复制相似问题