Фонг-зеркальное освещение в glsl (lwjgl)
В настоящее время я пытаюсь сделать зеркальное освещение на сфере, используя glsl и Phong-модель.
Вот так выглядит мой фрагментный шейдер:
#version 120
uniform vec4 color;
uniform vec3 sunPosition;
uniform mat4 normalMatrix;
uniform mat4 modelViewMatrix;
uniform float shininess;
// uniform vec4 lightSpecular;
// uniform vec4 materialSpecular;
varying vec3 viewSpaceNormal;
varying vec3 viewSpacePosition;
vec4 calculateSpecular(vec3 l, vec3 n, vec3 v, vec4 specularLight, vec4 materialSpecular) {
vec3 r = -l+2*(n*l)*n;
return specularLight * materialSpecular * pow(max(0,dot(r, v)), shininess);
}
void main(){
vec3 normal = normalize(viewSpaceNormal);
vec3 viewSpacePosition = (modelViewMatrix * vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)).xyz;
vec4 specular = calculateSpecular(sunPosition, normal, viewSpacePosition, vec4(0.3,0.3,0.3,0.3), vec4(0.3,0.3,0.3,0.3));
gl_FragColor = color+specular;
}
SunPosition не движется и имеет значение (2.0f, 3.0f, -1.0f).
Проблема в том, что изображение выглядит не так, как было бы целесообразно, если бы зеркальные расчеты были правильными.
Вот как это выглядит: http://i.imgur.com/Na2C6.png
Причина, по которой у меня нет никакого окружающего / излучающего / рассеянного освещения в этом коде, заключается в том, что я хочу, чтобы часть зеркального освещения работала первой.
Благодарю за любую помощь!
Редактировать: @Darcy Rayner То, что Indead очень помогло, кажется, что-то, что все еще не правильно...
Текущий код выглядит так:
Вершинный шейдер:
viewSpacePosition = (modelViewMatrix*gl_Vertex).xyz;
viewSpaceSunPosition = (modelViewMatrix*vec4(sunPosition,1)).xyz;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
viewSpaceNormal = (normalMatrix * vec4(gl_Position.xyz, 0.0)).xyz;
Фрагмент шейдера:
vec4 calculateSpecular(vec3 l, vec3 n, vec3 v, vec4 specularLight, vec4 materialSpecular) {
vec3 r = -l+2*(n*l)*n;
return specularLight * materialSpecular * pow(max(0,dot(r, v)), shininess);
}
void main(){
vec3 normal = normalize(viewSpaceNormal);
vec3 viewSpacePosition = normalize(viewSpacePosition);
vec3 viewSpaceSunPosition = normalize(viewSpaceSunPosition);
vec4 specular = calculateSpecular(viewSpaceSunPosition, normal, viewSpacePosition, vec4(0.7,0.7,0.7,1.0), vec4(0.6,0.6,0.6,1.0));
gl_FragColor = color+specular;
}
И сфера выглядит так:
с положением солнца: sunPosition = new Vector(12.0f, 15.0f, -1.0f);
1 ответ
Попробуйте не использовать gl_FragCoord, так как он хранится в экранных координатах (и я не думаю, что преобразование его с помощью modelViewMatrix вернет его для просмотра координат). Легче всего сделать, установить viewSpacePosition в вашем вершинном шейдере как:
// Change gl_Vertex to whatever attribute you are using.
viewSpacePosition = (modelViewMatrix * gl_Vertex).xyz;
Это должно привести вас к viewSpacePosition в координатах вида (т. Е. До применения проекции). Затем вы можете продолжить и нормализовать viewSpacePosition во фрагментном шейдере. Не уверен, что вы сохраняете солнечный вектор в мировых координатах, но вы, вероятно, захотите преобразовать его в пространство обзора, а затем нормализовать его. Попробуйте и посмотрите, что происходит, эти вещи, как правило, очень подвержены ошибкам.
gl_FragCoord
является входной переменной, которая содержит значения относительной координаты окна (x, y, z, 1/w) для фрагмента.... Это значение является результатом фиксированной функциональности, которая интерполирует примитивы после обработки вершин для генерации фрагментов.
Первые два значения (x,y) gl_FragCoord
содержит координаты центра пикселя, где фрагмент визуализируется. Например, при разрешении буфера кадра 800×600 фрагмент, отображаемый в левом нижнем углу, будет попадать в положение пикселя (0,5,0,5); фрагмент, выведенный в самый верхний правый угол, будет иметь координаты (799,5, 599,5). Третье значение (z) - это глубина фрагмента, отображенная в нелинейном диапазоне [0,1]. Он отображает глубины фрагментов от [znear, zfar] до [0,1].
Согласно этому, vec3 viewSpacePosition = (modelViewMatrix * vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)).xyz;
не делает то, что вы ожидаете. gl_FragCoord
позиция вершины, которая преобразуется gl_ModelViewProjectionMatrix
, интерполируется по беоцентрическим координатам фрагмента в примитиве треугольника и масштабируется по размеру области просмотра. Дальнейшее преобразование по матрице вида модели (modelView Matrix
) не имеет смысла.
Для расчета зеркального света (в нашем случае модель отражения Фонга) требуется 3 вектора:
- Нормальный вектор поверхности, который
normalize(viewSpaceNormal)
в этом случае. - Вектор от позиции фрагментов к камере. Камера имеет положение (0, 0, 0) в поле зрения. Таким образом, вектор представления
normalize(vec3(0.0) - viewSpacePosition)
, - Вектор света, который является вектором от позиции frgment к источнику света. Таким образом, вектор света рассчитывается по
normalize(viewSpaceSunPosition - viewSpacePosition)
,
Обратите внимание, так как расчет выполняется в пространстве вида (viewSpaceNormal
а такжеviewSpacePosition
находятся в поле зрения), вектор света и, следовательно, также положение источника света (viewSpaceSunPosition
), должен быть в поле зрения. Это означает, что положение источника света должно быть преобразовано матрицей вида, прежде чем он будет установлен на равномерную переменную.viewSpaceSunPosition
,
Обратите внимание, что матрица вида и матрица вида модели не должны быть одинаковыми. Матрица вида модели состоит из матрицы модели и матрицы вида. Обычно матрица модели содержит дополнительные преобразования для одного объекта (например, анимации на объекте в сцене).
Помимо этого, существует ошибка в расчете вектора отражения, формула расчета выглядит следующим образом:
incidentVector - 2.0 * dot(incidentVector, normalVector) * normalVector;
Обратите внимание, в GLSL есть функция reflect
, который сделал бы работу.
Вершина и фрагментный шейдер должны выглядеть примерно так:
Вершинный шейдер
varying vec3 viewSpacePosition;
varying vec3 viewSpaceNormal;
void main()
{
viewSpacePosition = (modelViewMatrix*gl_Vertex).xyz;
viewSpaceNormal = (normalMatrix * vec4(gl_Position.xyz, 0.0)).xyz;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
Фрагмент шейдера
varying vec3 viewSpacePosition;
varying vec3 viewSpaceNormal;
uniform vec3 viewSpaceSunPosition;
uniform float shininess;
vec4 calculateSpecular(vec3 L, vec3 N, vec3 V, vec4 specularLight, vec4 materialSpecular)
{
vec3 R = -L - 2.0 * dot(N, -L) * N;
// vec3 R = reflect( -L, N );
return specularLight * materialSpecular * pow(max(0,dot(R, V)), shininess);
}
void main()
{
vec3 N = normalize( viewSpaceNormal );
vec3 V = normalize( -viewSpacePosition );
vec3 L = normalize( viewSpaceSunPosition - viewSpacePosition );
vec4 specular = calculateSpecular(L, N, V, vec4(0.7,0.7,0.7,1.0), vec4(0.6,0.6,0.6,1.0));
gl_FragColor = color+specular;
}
Смотрите также ответы на эти вопросы: