LINUX.ORG.RU

OpenGL, проблема с графиком поверхности

 


0

2

Всем привет. Хочу нарисовать примерно такой график: ссылка. Вот обратите внимание на структуру поверхности, там цвет прямоугольников меняется ступенчато. Так вот, я хочу достичь такого же результата, но при этом не задавать цвета явно для каждой вершины (через VAO/VBO), а вычислять их на лету во фрагментном шейдере, в зависимости от значения Z.

И тут возникла такая проблема, посмотрите скриншот: https://ibb.co/QPpp1RB, wireframe: https://ibb.co/8NVTZZV. Получается такая вот ромбовидность.

Вот код шейдеров.

#version 330 core

uniform  mat4 u_mvp;
in       vec3 a_pos;
flat out vec3 vertex;

void main()
{
    vertex = a_pos;
    gl_Position = u_mvp * vec4(a_pos.xyz, 1.0f);
}

#version 330 core

flat in vec3 vertex;

vec3 get_color(float x)
{
    float t = (tanh(x) + 1) * 0.5f;
    vec3  a = vec3(0.0f, 0.0f, 1.0f);
    vec3  b = vec3(1.0f, 1.0f, 1.0f);
    return mix(a, b, t);
}

void main()
{
    gl_FragColor = vec4(get_color(vertex.z), 1.0f);
}

Почему так происходит? Как это можно исправить или сделать по-другому? Я только недавно стал заниматься комп. графикой, так что не судите :)


Если присмотреться, то в оригинале точно такая же ромбовидность. Смотрите на сторону, обращенную к углу квадрата.

COKPOWEHEU
()

Дык ромбовидность это плохо, но она есть и в исходном примере.

Что бы ее победить - цвет вершины должен зависеть только от координат самой вершины и не должен зависеть от других вершин треугольника. Тогда поле (цвета) будет непрерывным.

AntonI ★★★★
()

у тебя геометрия другая, ты строишь просто линиями, а в оригинале, судя по всему, геометрия строится по периметру, заполняя вершинами со всех сторон.

Int64 ★★★
()
Ответ на: комментарий от eve

тогда наверное используй не только vertex.z

anonymous
()
Ответ на: комментарий от eve

Да, только там не так сильно выражено.

Там квадраты поменьше и повернуты под другим углом, вот и все. Ну еще можно попробовать задавать цвет не по вершине, а по центру квадрата

COKPOWEHEU
()

Похоже, нашел решение. Поправил порядок обхода вершин и сделал glProvokingVertex(GL_FIRST_VERTEX_CONVENTION).

Был такой порядок (с ромбовидностью):

3,4     6
 +------+
 |\     |
 | \    |
 |  \   |
 |   \  |
 |    \ |
 +------+
 1     2,5

Стал такой:

 6     3,5
 +------+
 |     /|
 |    / |
 |   /  |
 |  /   |
 | /    |
 |/     |
 +------+
1,4     2
eve
() автор топика

Я так понял, порядок влиял на растеризацию.

eve
() автор топика
Ответ на: комментарий от eve

У вас что, на каждый квадрат два независимых треугольника используется? А всякие GL_TRIANGLE_STRIP или как оно там в современном OGL делается?

COKPOWEHEU
()
Ответ на: комментарий от COKPOWEHEU

Ну, я только начал. Всякие оптимизации потом будут.

eve
() автор топика
Ответ на: комментарий от salozar

Ага, а теперь добавь в это уравнение размер сетки, чтобы границы перехода цветов совпадали с границами четырехугольников сетки. Это лишние вычисления, которые вполне решаются flat-ом.

Но за идею спасибо, сразу об этом не подумал.

eve
() автор топика
Последнее исправление: eve (всего исправлений: 1)
Ответ на: комментарий от eve

так сразу бы писал что тебе надо сетку.

тогда решение такое:

пусть у тебя есть X*Y точек. массив вершин пусть будет float[], загоняешь в SSBO, и его же подключаешь как GL_ARRAY_BUFFER.

тебе надо рисовать GL_POINTS, а не треугольники. points должно быть (X-1)*(Y-1), и точка должна знать свой индекс gl_VertexId.

[code] buffer massiv { float data[]; } uniform int size; //здесь X, Y не нужно attribute float value;

out vec4 z_values;

void main() { z_values = vec4(value, data[gl_VertexId+1], data[gl_VertexId+size], data[gl_VertexId+size+1]); gl_Position = … } [/code] дальше в geometry shader из этой точки сделай 2 треугольника и 4 вершины. в нем же и на матрицу умножай.

и будет у тебя в фрагментном шейдере все 4 ближайшие Z.

salozar
()
Ответ на: комментарий от salozar

смысл в том, что каждый 4угольник мы рисуем как точку, которую в 4угольник раздуваем в геометрическом шейдере.

salozar
()
Ответ на: комментарий от salozar

можно вообще тупо в вершиннике писать

gl_Position.x = gl_VertexId%width; gl_Position.y = gl_VertexId/width; gl_Position.z = Z; gl_Position.w = 1.0;

и собирать значения Z в кучу в геометрическом шейдере

salozar
()
Ответ на: комментарий от salozar

Интересный вариант. Проверю позже, т. к. не у компа сейчас.

eve
() автор топика
Ответ на: комментарий от salozar

В общем, что-то я не осилил вашу идею, получается не то. Было бы здорово, если бы показали законченный код шейдеров, а то непонятно, что тут к чему. Может, есть ссылки по теме?

загоняешь в SSBO

А без этого можно обойтись? Мне желательно, чтобы работало на OpenGL 3.3

дальше в geometry shader из этой точки сделай 2 треугольника каждый 4угольник мы рисуем как точку, которую в 4угольник раздуваем

Тогда получится четырёхугольник, параллельный плоскости Oxy. Будут такие ступеньки как бы, а нужно, как тут. То есть, чтобы они были ориентированы соответственно.

и будет у тебя в фрагментном шейдере все 4 ближайшие Z и собирать значения Z в кучу в геометрическом шейдере

Геометрический шейдер с одним же значением работает. Не понял.

eve
() автор топика
Ответ на: комментарий от eve

проблема в том, что тебе надо хотя бы один аттрибут подавать на вход. в случае gl3.3 аттрибуты не нужны, можно текстурой обойтись, но некоторые драйвера крашатся когда нет аттрибутов вообще

рисуем (X-1)*(Y-1) GL_POINTS, текстура размером X*Y

//vertex
uniform int   size;//X-1
out     ivec2 s1;
void main() {
   s1 = ivec2(gl_VertexId % size.x, 
              gl_VertexId / size.x);
   gl_Position = vec4(0,0,0,1);
}

//geometry
layout(points) in;
layout(triangle_strip, max_vertices = 4​) out;
in  ivec2 s1;
uniform sampler2D data;
in  mat4  transform;
out vec4 color;

void main() {
   ivec2 tc = s1;
   float z1 = texelFetch(data, tc).r;
   vec4  p1 = vec4(vec2(tc.xy),z1,1.0)*transform;
   tc.x += 1;
   float z2 = texelFetch(data, tc).r;
   vec4  p2 = vec4(vec2(tc.xy),z2,1.0)*transform;
   tc.y += 1;
   float z3 = texelFetch(data, tc).r;
   vec4  p3 = vec4(vec2(tc.xy),z3,1.0)*transform;
   tc.x -= 1;
   float z4 = texelFetch(data, tc).r;
   vec4  p4 = vec4(vec2(tc.xy),z4,1.0)*transform;

   color = calculateColor(z1,z2,z3,z4);//как нибудь напиши
   gl_Position = p1;
   emitVertex();
   gl_Position = p2;
   emitVertex();
   gl_Position = p4;
   emitVertex();
   gl_Position = p3;
   emitVertex();
   endPrimitive();
}
//fragment
in vec4 color;
void main() {
  gl_FragColor = color;
}
salozar
()
Ответ на: комментарий от salozar

Только дошли руки проверить. Действительно, по вашему методу получается более контролируемый и точный результат. Правда, пришлось слегка доработать напильником, чтобы всё было выравнено. И все-таки решил использовать SSBO вместо текстуры.

Правда, производительность пока не сравнивал; вроде как у геометрических шейдеров с этим проблемы.

eve
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.