OpenGL — Как я могу раскрашивать объекты в пикселизированном виде с помощью шейдеров?

#opengl #glsl #shader #fragment-shader #vertex-shader

Вопрос:

Я пытаюсь найти способ осветить свой объект пиксельным способом с помощью шейдеров.

Чтобы успокоиться, моя цель-повернуть это:

в

В это:

из

Я пытался найти способы сделать это с помощью шейдера фрагментов, однако я никак не могу получить доступ к локальному положению фрагмента, чтобы определить, какому «поддельному пикселю» он будет принадлежать. У меня также была идея использовать геометрический шейдер для создания вершины для каждого из этих ящиков, но я подозреваю, что может быть лучший способ сделать это. Возможно ли это?

ИЗМЕНИТЬ: Это шейдеры, которые в настоящее время используются для объекта, иллюстрированного первым изображением:

вершинный шейдер:

 #version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTex;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 oColor; //Output of a color
out vec2 oTex; //Output of a Texture
out vec3 oPos; //Output of Position in space for light calculation
out vec3 oNormal; //Output of Normal vector for light calculation. 

void main(){
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    oColor = aColor;
    oTex = aTex;
    oPos = vec3(model * vec4(aPos, 1.0));
    oNormal = vec3(0, 0, -1); //Not being calculated at the moment.
}

 

шейдер фрагментов:

 #version 330 core

in vec3 oColor;
in vec2 oTex;
in vec3 oPos;  
in vec3 oNormal;  

out vec4 FragColor;

uniform sampler2D tex;
uniform vec3 lightColor; //Color of the light on the scene, there's only one
uniform vec3 lightPos; //Position of the light on the scene

void main(){
   //Ambient Light Calculation
   float ambientStrength = 0.1;
   //vec3 ambient = ambientStrength * lightColor * vec3(texture(tex, oTex));
   vec3 ambient = ambientStrength * lightColor;

   //Diffuse Light Calculation
   float diffuseStrength = 1.0;
   vec3 norm = normalize(oNormal);
   vec3 lightDir = normalize(lightPos - oPos);

   float diff = max(dot(norm, lightDir), 0.0);
   //vec3 diffuse = diff * lightColor* vec3(texture(tex, oTex)) * diffuseStrength;
   vec3 diffuse = diff * lightColor;

   //Specular Light Calculation
   float specularStrength = 0.25;
   float shinnyness = 8;
   vec3 viewPos = vec3(0, 0, -10);

   vec3 viewDir = normalize(viewPos - oPos);
   vec3 reflectDir = reflect(-lightDir, norm);    

   float spec = pow(max(dot(viewDir, reflectDir), 0.0), shinnyness);
   vec3 specular = specularStrength * spec * lightColor;  

   //Result Light
   vec3 result = (ambient diffuse specular) * oColor;
   FragColor = vec4(result, 1.0f);
}

 

Комментарии:

1. «однако я никак не могу получить доступ к локальному положению фрагмента, чтобы определить, какому «поддельному пикселю» он будет принадлежать» Почему бы и нет? Вот в чем gl_FragCoord дело. Не уверен, что вы имеете в виду под «локальным положением», но если вам нужен какой-то пиксельный растр, не выровненный с пикселями оконного пространства, вы можете рассчитать положение в этом растре в вершинном шейдере и передать его интерполированным в фрагментный шейдер.

2. Однако gl_FragCoord указывает положение относительно окна. Я хотел бы найти способ определить положение относительно самого объекта, если это имеет смысл.

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

4. Пожалуйста, включите некоторый код, чтобы показать, как вы создаете изображение без пикселей.

5. Вы должны показать нам свой код шейдера фрагментов.

Ответ №1:

Освещение зависит от oPos . Вам нужно «каскадировать» позицию. например:

 vec3 pos = vec3(round(oPos.xy * 10.0) / 10.0, oPos.z);
 

В следующем случае используйте pos вместо oPos .

Обратите внимание, что это работает только в том случае, если oPos это позиция в пространстве просмотра, соответственно, если плоскость XY системы координат oPos параллельна плоскости XY вида.


В качестве альтернативы вы можете вычислить позицию a в зависимости от gl_FragCoord .

Добавьте однородную переменную с разрешением экрана:

 uniform vec2 resolution;
 

Вычислять pos в зависимости от resolution и gl_FragCoord :

 vec3 pos = vec3(round(20.0 * gl_FragCoord.xy/resolution.y) / 20.0, oPos.z);
 

Если вы хотите выровнять внутренние квадраты с объектом, вам нужно ввести координаты текстуры. Где левая нижняя координата объекта равна (0, 0), а правая верхняя — (1, 1).

Комментарии:

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

2. @bieux Если вы хотите выровнять внутренние квадраты с объектом, вам нужно ввести координаты текстуры. Где левая нижняя координата объекта равна (0, 0), а правая верхняя — (1, 1). v