Я хочу визуализировать данные симуляции в openGL.
Мои данные состоят из положений частиц (x, y, z), где каждая частица имеет некоторые свойства (такие как плотность, температура, …), которые будут использоваться для окрашивания. Эти (SPH) частицы (от 100 тыс. До нескольких миллионов), сгруппированные вместе, на самом деле представляют планеты, если вам интересно. Я хочу сделать эти частицы маленькими трехмерными сферами и добавить рассеянное и зеркальное освещение.
Я видел, что это обычно делается в пространстве зрения, которое также очень интуитивно понятно. Однако: нормали в разных положениях фрагмента вычисляются в фрагментном шейдере в координатах пространства клипа (см. Прилагаемый фрагментный шейдер). Могу ли я на самом деле преобразовать их «назад» в пространство просмотра, чтобы сделать молниеносные вычисления в пространстве обзора для всех фрагментов? Будет ли какое-то преимущество по сравнению с этим в пространстве клипа?
PS: мне не нужна модель матрицы, так как все частицы уже на месте.
//VERTEX SHADER
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 2) in float density;
uniform float radius;
uniform vec3 lightPos;
uniform vec3 viewPos;
out vec4 lightDir;
out vec4 viewDir;
out vec4 viewPosition;
out vec4 posClip;
out float vertexColor;// transformation matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
lightDir = projection * view * vec4(lightPos - position, 1.0f);
viewDir = projection * view * vec4(viewPos - position, 1.0f);
viewPosition = projection * view * vec4(lightPos, 1.0f);
posClip = projection * view * vec4(position, 1.0f);
gl_Position = posClip;
gl_PointSize = radius;
vertexColor = density;
}
И фрагмент шейдера, где нормальные и диффузные / зеркальные молнии вычисляются в пространстве клипа:
//FRAGMENT SHADER
#version 330 core
in float vertexColor;
in vec4 lightDir;
in vec4 viewDir;
in vec4 posClip;
in vec4 viewPosition;
uniform vec3 lightColor;
vec4 colormap(float x); // returns vec4(r, g, b, a)
out vec4 vFragColor;void main(void)
{
// AMBIENT LIGHT
float ambientStrength = 0.0;
vec3 ambient = ambientStrength * lightColor;
// Normal calculation done in clip space (first from texture (gl_PointCoord 0 to 1) coord to NDC( -1 to 1))
vec3 normal;
normal.xy = gl_PointCoord * 2.0 - vec2(1.0); // transform from 0->1 point primitive coords to NDC -1->1
float mag = dot(normal.xy, normal.xy); // sqrt(x=1) = sqrt(x)
if (mag > 1.0) // discard fragments outside sphere
discard;
normal.z = sqrt(1.0 - mag); // because x^2 + y^2 + z^2 = 1
// DIFFUSE LIGHT
float diff = max(0.0, dot(vec3(lightDir), normal));
vec3 diffuse = diff * lightColor;
// SPECULAR LIGHT
float specularStrength = 0.1;
vec3 viewDir = normalize(vec3(viewPosition) - vec3(posClip));
vec3 reflectDir = reflect(-vec3(lightDir), normal);
float shininess = 64;
float spec = pow(max(dot(vec3(viewDir), vec3(reflectDir)), 0.0), shininess);
vec3 specular = specularStrength * spec * lightColor;
vFragColor = colormap(vertexColor / 8) * vec4(ambient + diffuse + specular, 1);
}
Какой-то странный эффект: в этот момент источник света фактически находится за левой планетой (он немного выступает вверху слева вверху), но все же происходят рассеянные и зеркальные эффекты. Эта сторона должна быть довольно темной! знак равно
Также в этот момент я получаю ошибку glError: 1282 во фрагментном шейдере и не знаю, откуда она взялась, так как программа шейдера фактически компилируется и запускается, какие-либо предложения? 🙂
То, что вы рисуете, на самом деле не является сферами. Они просто похожи на них издалека. Это абсолютно нормально, если ты в порядке с этим. Если вам нужны геометрически правильные сферы (с правильными размерами и с правильной проекцией), вам нужно сделать правильное лучевое вещание. это кажется, всеобъемлющее руководство по этой теме.
В конце концов, это зависит от вас. Система координат просто должна соответствовать некоторым требованиям. Это должно быть сохранение угла (потому что освещение — все о углах). И если вам нужно затухание на основе расстояния, оно также должно сохранять дистанцию. Системы координат мира и представления обычно отвечают этим требованиям. Клип пространство не подходит для расчета освещения, так как не сохраняются ни углы, ни расстояния. Более того, gl_PointCoord
не входит ни в одну из обычных систем координат. Это его собственная система координат, и вы должны использовать ее только вместе с другими системами координат, если знаете их отношение.
Сетки абсолютно не подходят для визуализации сфер. Как упомянуто выше, лучевое вещание или некоторое приближение пространства экрана — лучший выбор. Вот пример шейдера, который я использовал в своих проектах:
#version 330
out vec4 result;
in fData
{
vec4 toPixel; //fragment coordinate in particle coordinates
vec4 cam; //camera position in particle coordinates
vec4 color; //sphere color
float radius; //sphere radius
} frag;
uniform mat4 p; //projection matrix
void main(void)
{
vec3 v = frag.toPixel.xyz - frag.cam.xyz;
vec3 e = frag.cam.xyz;
float ev = dot(e, v);
float vv = dot(v, v);
float ee = dot(e, e);
float rr = frag.radius * frag.radius;
float radicand = ev * ev - vv * (ee - rr);
if(radicand < 0)
discard;
float rt = sqrt(radicand);float lambda = max(0, (-ev - rt) / vv); //first intersection on the ray
float lambda2 = (-ev + rt) / vv; //second intersection on the ray
if(lambda2 < lambda) //if the first intersection is behind the camera
discard;
vec3 hit = lambda * v; //intersection point
vec3 normal = (frag.cam.xyz + hit) / frag.radius;
vec4 proj = p * vec4(hit, 1); //intersection point in clip space
gl_FragDepth = ((gl_DepthRange.diff * proj.z / proj.w) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;
vec3 vNormalized = -normalize(v);
float nDotL = dot(vNormalized, normal);
vec3 c = frag.color.rgb * nDotL + vec3(0.5, 0.5, 0.5) * pow(nDotL, 120);
result = vec4(c, frag.color.a);
}
Перспективное деление не применяется к вашим атрибутам. GPU выполняет перспективное деление данных, через которые вы передаете gl_Position
на пути к преобразованию их в пространство экрана. Но вы никогда не увидите эту разделенную перспективу позицию, если не сделаете это сами.
Это может быть результатом того, что вы смешиваете разные системы координат или выполняете расчеты освещения в пространстве клипа. Кстати, зеркальная часть обычно не умножается на цвет материала. Это свет, который отражается прямо на поверхности. Он не проникает в поверхность (что может поглощать некоторые цвета в зависимости от материала). Вот почему эти блики обычно белые (или любой другой светлый цвет), даже на черных объектах.
Других решений пока нет …