2015-02-22 9 views
0

Я пытаюсь перепрофилировать пример шейдера высотной карты, найденный here, в том, что будет работать с 32-битной точностью вместо 8. Код незавершенного выполнения находится на github: https://github.com/bgourlie/three_heightmapРеализация 32-битного шейдера вершинной вершины вершин в трехjs

Карта высот создается в .NET. Высоты находятся в пределах 0f ... 200F и преобразуется в значение 32-битного цвета (Color struct Unity в) с использованием следующего метода:

private static Color DepthToColor(float height) 
{ 
    var depthBytes = BitConverter.GetBytes(height); 
    int enc = BitConverter.ToInt32(depthBytes, 0); 
    return new Color((enc >> 24 & 255)/255f, (enc >> 16 & 255)/255f, (enc >> 8 & 255)/255f, 
      (enc & 255)/255f); 
} 

Цветовые данные кодируются как PNG, с результатом глядя, как это:

enter image description here

вершинный шейдер принимает эти данные изображения и coverting значения RBGA обратно к исходному значению высоты (с использованием техники ответил на мой вопрос here):

uniform sampler2D bumpTexture; // defined in heightmap.js 
varying float vAmount; 
varying vec2 vUV; 

void main() 
{ 
    vUV = uv; 
    vec4 bumpData = texture2D(bumpTexture, uv); 
    vAmount = dot(bumpData, vec4(1.0, 255.0, 65025.0, 16581375.0)); 
    // Uncomment to see a "flatter" version 
    //vAmount = dot(bumpData, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/160581375.0)); 

    // move the position along the normal 
    vec3 newPosition = position + normal * vAmount; 
    gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); 
} 

Результат, безусловно, перепутались:

enter image description here

я могу сделать это льстить, изменив эту строку:

vAmount = dot(bumpData, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0)); 

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

enter image description here

Я предполагаю, что я делаю несколько вещей неправильно, я просто не знаю, что. Я не уверен, правильно ли кодирую исходный флоат. Я не уверен, правильно ли я его декодирую в вершинном шейдере (значение, которое я получаю, конечно, находится за пределами диапазона 0 ... 200). Я также не очень опытен в 3D-графике в целом. Поэтому любые рекомендации относительно того, что я делаю неправильно, или вообще, как добиться этого, будут очень признательны.

Опять же, самодостаточная работа в прогрессе код можно найти здесь: https://github.com/bgourlie/three_heightmap

+0

Шум в «png» карты высоты определенно выглядит неправильно. Даже при использовании всех каналов для глубины вы должны хотя бы иметь гладкий градиент в пределах одного цвета, и если у вас есть шум, это не должно быть экстремальным. В связи с этим я предлагаю сузить поиск до той части, где вы создаете карту высот. Я бы предположил, что вы где-то попали в неинициализированную память. –

+0

Я предположил, что шум был результатом использования альфа-канала (при этом ожидая некоторый шум). Вот результирующая карта высот без использования альфа-канала: http://i.imgur.com/l3zcpcG.jpg. По общему признанию, градиенты все еще не выглядят правильно, учитывая высоты, которые они должны представлять. –

+0

Извините, я думаю, что у меня было неправильное предположение: белые точки на самом деле прозрачные пиксели? Первоначально у png была прозрачная прозрачность? Вы сохранили его как png-8, чтобы показать его здесь, и альфа-канал был потерян? Это объясняет белые точки. Если вы использовали это экспортированное изображение для шейдера, вам нужно будет проверить создание PNG, если вы можете выбирать между 8 и 24. (Использование png с альфа-каналом может быть проблемой в любом случае, поскольку сжатие может создавать проблемы в областях, где изображение, если оно полностью прозрачно). –

ответ

1

Ваш цвет:

return new Color((enc >> 24 & 255)/255f, (enc >> 16 & 255)/255f, (enc >> 8 & 255)/255f, 
     (enc & 255)/255f); 

... содержит наиболее значимый байт enc в r, второй по значимости в g и т.д.

Это:

vAmount = dot(bumpData, vec4(1.0, 255.0, 65025.0, 160581375.0)); 

строит vAmount с r в наименее значимом байте, g в следующем - очень значительная и т. д. (хотя мультимножества должны быть 256, 65536 и т. д. *). Таким образом, байты находятся в неправильном порядке.Пологая версия:

vAmount = dot(bumpData, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/160581375.0)); 

получает байты в правильном порядке, но шкалы выходных значений в диапазон [0.0, 1.0], который, вероятно, почему она выглядит практически плоской.

Поэтому переключите порядок кодирования или декодирования байтов и выберите подходящий масштаб.

(*) думаю об этом так: наименьшее число, которое может идти по любому каналу, равно 1.0/255.0. Наименее значимый канал будет находиться в диапазоне [0, 1.0] - от 0/255.0 до 255.0/255.0. Вы хотите масштабировать следующий канал, чтобы его наименьшее значение было следующим в этом масштабе. Поэтому его наименьшее значение должно быть 256/255.0. Поэтому вам нужно повернуть 1.0/255.0 в 256.0/255.0. Вы достигаете этого путем умножения на 256, а не на 255.

+0

Действительно оцените объяснение. Это заставило меня понять, как и почему я кодирую карту высоты в ужасно наивной, неправильной форме. По крайней мере сейчас, когда я получу это, я буду знать, как правильно получить значения ARGB в шейдере. Еще раз спасибо! –

1

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