Создание точно текстурированной сетки глобуса - PullRequest
0 голосов
/ 12 мая 2018

Я пытаюсь смоделировать глобус в javascript с помощью WebGL и начал делать это, следуя этому руководству .

Текстура, отображаемая на сетке, должна иметь минимальное искажение.Чтобы избежать искаженной области, расстояния и формы, я использую прерванную синусоидальную проекцию карты вместо равноугольной.Для тестирования я использую текстуру земли, найденную на этом сайте .

Проблема, с которой я сталкиваюсь, состоит в том, что вершины отображаются за пределами ожидаемых границ текстуры.Чем выше количество вершин I, тем меньше видимая «внешняя» текстура (слева направо счетчики широты и долготы удваиваются с каждым шагом):

preview

Формула проекции должна быть правильной, я смог создать файл трафарета, чтобы убедиться, что в c # tho я не настолько привык к js: map stencil

Итак, мой вопрос: как улучшить приближение сферы и не показывать пиксели за пределами области предполагаемой текстуры?

Соответствующий код:

// 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;
}

1 Ответ

0 голосов
/ 12 мая 2018

Ваши текстурные координаты не являются смежными по всей вашей сфере.Между ними есть большие разрывы.Таким образом, вы не можете создать свою сферу, используя обычные механизмы;Вы должны сгенерировать его на основе того, где вы хотите, чтобы ваши текстурные координаты.То есть геометрия вашей сетки должна соответствовать непрерывности вашей текстуры.

Ваша текстура содержит эти полосы данных.Таким образом, эти полосы должны быть основой генерации вашей вершины, включая позиции.Поэтому сфера для вас - это не «сфера», а последовательность этих парабалоидных полос, которые образуют сферическую форму.Каждая отдельная полоса должна быть отделена от других;они не могут повторно использовать вершины.

В то же время вы должны убедиться, что края каждой полосы генерируют значения положения, которые идентичны их соседней полосе.В противном случае вы можете получить промежутки между полосами.

Еще одна вещь, которую вам нужно сделать, это убедиться, что ваша текстура имеет соответствующие данные по краям полос.Интерполяция текстурных координат между треугольниками на краях, а также фильтрация текстур иногда позволяют получить доступ к значениям в «белых» областях текстуры.Таким образом, вам нужно иметь добавленную информацию в один пиксель по краям полос, чтобы фильтрация не привлекала нежелательные данные.И это должно быть сделано на каждом уровне mipmap.

Обычная идея - просто повторить соседний тексель.

В общем, однако, если вы хотите текстурировать глобус (и у вас естьвозможность генерировать текстуру земного шара, как вы хотите), используйте кубическую карту.Этот метод произведет наименьшее искажение и потребует наименьших усилий (кроме получения самой текстуры кубической карты).Кроме того, вам даже не нужны текстурные координаты;просто используйте интерполированные нормали для координат текстуры.

...