Follow examples online, here is the code in the vertex shader:
The attributes are defined as follows:
// Option 1:
layout (location = 0) in vec3 colorvec3;
layout (location = 1) in mat4x2 xyInBaseImageMat4x2;
out vec2 xyInElementTextureImage;
out vec3 elementTintColorVec3;
In the vertex shader, they are used:
// Option 1
vec2 xyInBaseImageVec2 = xyInBaseImageMat4x2[gl_VertexID];
gl_Position = vec4(xyInBaseImageVec2, 0.0, 1.0);
elementTintColorVec3 = colorvec3;
But the result is weird, totally random. Sometimes it is black, sometimes it is random shape.
But if I change to use:
// Option 2:
layout (location = 0) in vec3 colorvec3;
layout (location = 1) in vec2 xyInBaseImageVec2_p0;
layout (location = 2) in vec2 xyInBaseImageVec2_p1;
layout (location = 3) in vec2 xyInBaseImageVec2_p2;
layout (location = 4) in vec2 xyInBaseImageVec2_p3;
out vec2 xyInElementTextureImage;
out vec3 elementTintColorVec3;
In the vertex shader:
// Option 2:
vec2 xyInBaseImageVec2;
if (gl_VertexID==0) {
xyInBaseImageVec2 = xyInBaseImageVec2_p0;
} else if (gl_VertexID==1) {
xyInBaseImageVec2 = xyInBaseImageVec2_p1;
} else if (gl_VertexID==2) {
xyInBaseImageVec2 = xyInBaseImageVec2_p2;
} else if (gl_VertexID==3) {
xyInBaseImageVec2 = xyInBaseImageVec2_p3;
}
gl_Position = vec4(xyInBaseImageVec2, 0.0, 1.0);
elementTintColorVec3 = colorvec3;
Then it works as desired.
The data buffer for the color and vertex positions are the same for the two examples. [Update: add the feeding code below]
// refer to the code in: http://sol.gfxile.net/instancing.html
// refer to the code in: http://www.gamedev.net/page/resources/_/technical/opengl/opengl-instancing-demystified-r3226
// Case: instaced vertex positions. So each instance should have 4 vec2
{
// in mat4x2; later try in vec2[4] xyInBaseImage_vec2list if mat4x2 does not work.
GLuint instanceVBO = instancevbo_4pts;
int pos = 1;
int componentsSize = 2; // vec2 has 2 components
// if it is mat, then componentsNum is the column number of the matrix; for example, for mat4x2 it is 4
int componentsNum = 4; // mat4x2
GLenum type = GL_FLOAT;
GLboolean normalized = GL_FALSE;
GLsizei stride = componentsSize * sizeof(GL_FLOAT) * componentsNum;
char* pointerFirstComponentOffset = 0;
int offsetInteger = 0;
int byteSizeOfOneVertexAttribute = componentsSize * sizeof(GL_FLOAT);
GLuint divisor = 1; // 0 not instance
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
for (int i = 0; i < componentsNum; i++) {
glEnableVertexAttribArray(pos + i);
// the offset can also be: (void*) (offsetInteger + i * byteSizeOfOneVertexAttribute)
glVertexAttribPointer(pos + i, componentsSize, type,
normalized, stride, pointerFirstComponentOffset + i * byteSizeOfOneVertexAttribute );
glVertexAttribDivisor(pos + i, divisor);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
What's wrong with the first option?
Related
Does anyone know why I keep getting the error that says:
The ♦ shader uses varying _I;DATA;g_mapCoord, but previous shader does not write to it.
The ♦ shader uses varying _I;DATA;worldPosition, but previous shader does not write to it.
Take a look at my shaders here.
Vertex
#version 430
layout (location = 0) in vec2 position0;
out DATA {
vec2 v_mapCoord;
vec3 worldPosition;
} Out;
uniform vec3 u_cameraPosition;
uniform mat4 u_localMatrix;
uniform mat4 u_worldMatrix;
uniform float u_scaleY;
uniform int u_lod;
uniform vec2 u_index;
uniform float u_gap;
uniform vec2 u_location;
uniform sampler2D s_heightmap;
uniform int u_lodMorphArea[8];
float morphLatitude(vec2 position)
{
//not important code
return 0;
}
float morphLongitude(vec2 position)
{
//not important code
return 0;
}
vec2 morph(vec2 localPosition, int morph_area){
//not important code
return vec2(0);
}
void main()
{
vec2 localPosition = (u_localMatrix * vec4(position0.x,0,position0.y,1)).xz;
if (u_lod > 0) {
localPosition += morph(localPosition, u_lodMorphArea[u_lod-1]); // Translate position by morphing vector
}
float height = texture(s_heightmap, localPosition).r;
Out.v_mapCoord = localPosition;
vec4 _worldPosition = u_worldMatrix * vec4(localPosition.x, height, localPosition.y,1);
Out.worldPosition = _worldPosition.xyz;
gl_Position = u_worldMatrix * vec4(localPosition.x, height, localPosition.y,1);
}
Fragment
#version 430
layout (location = 0) out vec4 outputColor;
in DATA {
vec2 g_mapCoord;
vec3 worldPosition;
} In;
const vec3 lightDirection = vec3(-0.2, -1.0, -0.2);
const float intensity = 1.2;
uniform sampler2D s_textureNormal;
uniform sampler2D s_textureWater;
uniform sampler2D s_textureLand;
float diffuse(vec3 direction, vec3 normal, float intensity)
{
return max(0.01, dot(normal, -direction) * intensity);
}
void main()
{
vec3 normal = texture(s_textureNormal, In.g_mapCoord).rgb;
float diff = diffuse(lightDirection, normal, intensity);
outputColor = vec4(1,0,0,1);
}
Geom
#version 430
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
in vec2 te_mapCoord[];
out vec2 g_mapCoord;
uniform mat4 u_viewProjection;
void main() {
for (int i = 0; i < gl_in.length(); ++i)
{
vec4 position = gl_in[i].gl_Position;
gl_Position = u_viewProjection * position;
g_mapCoord = te_mapCoord[i];
EmitVertex();
}
EndPrimitive();
}
TCS
#version 430
layout(vertices = 16) out;
in DATA {
vec2 v_mapCoord;
vec3 worldPosition;
} In[];
out vec2 tc_mapCoord[];
const int AB = 2;
const int BC = 3;
const int CD = 0;
const int DA = 1;
uniform int u_tessellationFactor;
uniform float u_tessellationSlope;
uniform float u_tessellationShift;
uniform vec3 u_cameraPosition;
// Calculate tessellation levels
float lodFactor(float dist)
{
float tessellationLevel = max(0.0, u_tessellationFactor/pow(dist, u_tessellationSlope) + u_tessellationShift);
return tessellationLevel;
}
void main()
{
if (gl_InvocationID == 0){
// Calculate mid points of the edges of the quad
vec3 abMid = vec3(gl_in[0].gl_Position + gl_in[3].gl_Position)/2.0; //Bottom left, Bottom right
vec3 bcMid = vec3(gl_in[3].gl_Position + gl_in[15].gl_Position)/2.0; //Bottom right Top right
vec3 cdMid = vec3(gl_in[15].gl_Position + gl_in[12].gl_Position)/2.0; //Top right, Top left
vec3 daMid = vec3(gl_in[12].gl_Position + gl_in[0].gl_Position)/2.0; //Top left, Bottom left
// Calculate distance between camera and mid points of the edges of the quad
float distanceAB = distance(abMid, u_cameraPosition);
float distanceBC = distance(bcMid, u_cameraPosition);
float distanceCD = distance(cdMid, u_cameraPosition);
float distanceDA = distance(daMid, u_cameraPosition);
// Tesselation levels used by tessellation primitive generator (define how much tessellation to apply to the patch). Value between 1 and gl_MaxTessGenLevel, depending on lodFactor.
gl_TessLevelOuter[AB] = mix(1, gl_MaxTessGenLevel, lodFactor(distanceAB));
gl_TessLevelOuter[BC] = mix(1, gl_MaxTessGenLevel, lodFactor(distanceBC));
gl_TessLevelOuter[CD] = mix(1, gl_MaxTessGenLevel, lodFactor(distanceCD));
gl_TessLevelOuter[DA] = mix(1, gl_MaxTessGenLevel, lodFactor(distanceDA));
gl_TessLevelInner[0] = (gl_TessLevelOuter[BC] + gl_TessLevelOuter[DA])/4;
gl_TessLevelInner[1] = (gl_TessLevelOuter[AB] + gl_TessLevelOuter[CD])/4;
}
tc_mapCoord[gl_InvocationID] = In[gl_InvocationID].v_mapCoord; // Just pass to the next stage
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
}
TES
#version 430
layout(quads, fractional_odd_spacing, cw) in;
in vec2 tc_mapCoord[];
out vec2 te_mapCoord;
uniform sampler2D s_heightmap;
uniform float u_scaleY;
void main(){
float u = gl_TessCoord.x;
float v = gl_TessCoord.y;
// Compute new position for each tessellated vertex within the patch. gl_in with index 12, 0, 3, 15 are corners of the patch.
vec4 position = ((1 - u) * (1 - v) * gl_in[12].gl_Position + u * (1 - v) * gl_in[0].gl_Position + u * v * gl_in[3].gl_Position +(1 - u) * v * gl_in[15].gl_Position);
vec2 mapCoord = ((1 - u) * (1 - v) * tc_mapCoord[12] + u * (1 - v) * tc_mapCoord[0] + u * v * tc_mapCoord[3] +(1 - u) * v * tc_mapCoord[15]);
float height = texture(s_heightmap, mapCoord).r;
height *= u_scaleY;
position.y = height;
te_mapCoord = mapCoord;
gl_Position = position;
}
Can anyone help me find the error here which is why I'm getting that error message?
When you introduce a geometry shader you need to pass the varyings for the fragment shader from the geometry shader, not the vertex shader.
You can see how your geometry shader doing this:
out vec2 g_mapCoord;
is incompatible with your fragment shader expecting this:
in DATA {
vec2 g_mapCoord;
vec3 worldPosition;
} In;
Related question and subsequent answers here.
I'm creating a few glowing particles in raylib using shaders and the particles are supposed to move along with the mouse but when compiling it gets stuck to the bottom left corner and the particles dont move.
How it Looks
The c++ code
#include <raylib.h>
#include <vector>
const int W = 400;
const int H = 400;
std::vector<Vector2> particle;
float remap(float value, float low1, float high1, float low2, float high2) {
return low2 + (value - low1) * (high2 - low2) / (high1 - low1);
}
int main() {
SetConfigFlags( FLAG_WINDOW_RESIZABLE );
InitWindow(W, H, "FireWorks");
Shader shader = LoadShader("../assets/vert.glsl", "../assets/frag.glsl");
Texture2D texture = LoadTextureFromImage(GenImageColor(W, H, BLUE));
int resolLoc = GetShaderLocation(shader, "resolution");
int particleLoc = GetShaderLocation(shader, "particle");
int particleCountLoc = GetShaderLocation(shader, "particleCount");
float res[2] = {(float)W, (float)H};
SetShaderValue(shader, resolLoc, res, SHADER_UNIFORM_VEC2);
SetTargetFPS(60);
while (!WindowShouldClose()) {
BeginDrawing();
ClearBackground(BLACK);
particle.push_back(Vector2{(float)GetMouseX(), (float)GetMouseY()});
int removeCount = 1;
for (int i = 0; i < removeCount; i++) {
if (particle.size() == 0) break;
if (particle.size() > 30) {
particle.erase(particle.begin() + i);
}
}
BeginShaderMode(shader);
float particles[30][2];
for ( int i = 0; i < particle.size(); i++) {
particles[i][0] = remap(particle[i].x, 0, W, 0.0, 1.0);
particles[i][1] = remap(particle[i].y, 0, H, 1.0, 0.0);
}
int pSize = particle.size();
SetShaderValue(shader, particleCountLoc, &pSize, SHADER_UNIFORM_INT);
SetShaderValue(shader, particleLoc, particles, SHADER_UNIFORM_VEC2);
DrawTextureRec(texture, (Rectangle) { 0, 0, (float)texture.width, (float) -texture.height }, (Vector2) { 0, 0}, RAYWHITE);
DrawRectangle(0, 0, W, H, BLACK);
EndShaderMode();
EndDrawing();
}
UnloadTexture(texture);
UnloadShader(shader);
CloseWindow();
return 0;
}
The Vertex Shader
#version 330
// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
// Output vertex attributes (to fragment shader)
out vec2 fragTexCoord;
out vec4 fragColor;
// NOTE: Add here your custom variables
void main()
{
// Send vertex attributes to fragment shader
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
// Calculate final vertex position
gl_Position = mvp * vec4(vertexPosition, 1.0);
}
The Fragment Shader
#version 330
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
in vec4 fragColor;
// Input uniform values
uniform sampler2D texture0;
uniform vec4 colDiffuse;
// Output fragment color
out vec4 finalColor;
// NOTE: Add here your custom variables
uniform vec2 resolution;
uniform int particleCount;
uniform vec2 particle[30];
void main() {
// Texel color fetching from texture sampler
vec4 texelColor = texture(texture0, fragTexCoord);
vec2 st = gl_FragCoord.xy / resolution.xy;
float r = 0.0;
float g = 0.0;
float b = 0.0;
for (int i = 0; i < 30; i++) {
if (i < particleCount) {
vec2 particlePos = particle[i];
float value = float(i) / distance(st, particlePos.xy) * 0.00015;
g += value * 0.5;
b += value;
}
}
finalColor = vec4(r, g, b, 1.0) * texelColor * colDiffuse;
}
The JS version of the code (which works) is here.
If you could point me in the right direction it'd be great.
The uniform particle is of type vec2[30]. An uniform array can needs to be set with SetShaderValueV instead of SetShaderValue:
SetShaderValue(shader, particleLoc, particles, SHADER_UNIFORM_VEC2);
SetShaderValueV(shader, particleLoc, particles[0], SHADER_UNIFORM_VEC2, 30);
I'd like to render object to see the inside of the box.
I used Phong Shading.
When I draw object with glPolygonMode(GL_FRONT_AND_BACK, GL_LINE), an image looks like this:
But When I used glPolygonMode(GL_FRONT_AND_BACK, GL_FILL), the image looks like this:
I'd like to shade only rectangle part. So I want to see the objects inside the box. This is fragment shade code, and I think it works well. But I don't know why i can't see inside.
#version 400
struct LIGHT {
vec4 position; // assume point or direction in EC in this example shader
vec4 ambient_color, diffuse_color, specular_color;
vec4 light_attenuation_factors; // compute this effect only if .w != 0.0f
vec3 spot_direction;
float spot_exponent;
float spot_cutoff_angle;
bool light_on;
};
struct MATERIAL {
vec4 ambient_color;
vec4 diffuse_color;
vec4 specular_color;
vec4 emissive_color;
float specular_exponent;
};
uniform vec4 u_global_ambient_color;
#define NUMBER_OF_LIGHTS_SUPPORTED 4
uniform LIGHT u_light[NUMBER_OF_LIGHTS_SUPPORTED];
uniform MATERIAL u_material;
const float zero_f = 0.0f;
const float one_f = 1.0f;
in vec3 v_position_EC;
in vec3 v_normal_EC;
layout (location = 0) out vec4 final_color;
vec4 lighting_equation(in vec3 P_EC, in vec3 N_EC) {
vec4 color_sum;
float local_scale_factor, tmp_float;
vec3 L_EC;
color_sum = u_material.emissive_color + u_global_ambient_color * u_material.ambient_color;
for (int i = 0; i < NUMBER_OF_LIGHTS_SUPPORTED; i++) {
if (!u_light[i].light_on) continue;
local_scale_factor = one_f;
if (u_light[i].position.w != zero_f) { // point light source
L_EC = u_light[i].position.xyz - P_EC.xyz;
if (u_light[i].light_attenuation_factors.w != zero_f) {
vec4 tmp_vec4;
tmp_vec4.x = one_f;
tmp_vec4.z = dot(L_EC, L_EC);
tmp_vec4.y = sqrt(tmp_vec4.z);
tmp_vec4.w = zero_f;
local_scale_factor = one_f/dot(tmp_vec4, u_light[i].light_attenuation_factors);
}
L_EC = normalize(L_EC);
if (u_light[i].spot_cutoff_angle < 180.0f) { // [0.0f, 90.0f] or 180.0f
float spot_cutoff_angle = clamp(u_light[i].spot_cutoff_angle, zero_f, 90.0f);
vec3 spot_dir = normalize(u_light[i].spot_direction);
tmp_float = dot(-L_EC, spot_dir);
if (tmp_float >= cos(radians(spot_cutoff_angle))) {
tmp_float = pow(tmp_float, u_light[i].spot_exponent);
}
else
tmp_float = zero_f;
local_scale_factor *= tmp_float;
}
}
else { // directional light source
L_EC = normalize(u_light[i].position.xyz);
}
if (local_scale_factor > zero_f) {
vec4 local_color_sum = u_light[i].ambient_color * u_material.ambient_color;
tmp_float = dot(N_EC, L_EC);
if (tmp_float > zero_f) {
local_color_sum += u_light[i].diffuse_color*u_material.diffuse_color*tmp_float;
vec3 H_EC = normalize(L_EC - normalize(P_EC));
tmp_float = dot(N_EC, H_EC);
if (tmp_float > zero_f) {
local_color_sum += u_light[i].specular_color
*u_material.specular_color*pow(tmp_float, u_material.specular_exponent);
}
}
color_sum += local_scale_factor*local_color_sum;
}
}
return color_sum;
}
void main(void) {
final_color = lighting_equation(v_position_EC, normalize(v_normal_EC)); // for normal rendering
}
I'm currently working with skeletal animation and I'm really close to getting it working. Currently, I have a struct that has a matrix with 100 spots ( this is so that I can max have 100 joints ) like so :
struct skelShader {
glm::mat4 currentJointTrans[100];
};
The struct should be binded in the shader, I've done it like this:
glGenBuffers(1, &sksBuff);
glBindBuffer(GL_UNIFORM_BUFFER, sksBuff);
// bind buffer to work further with it...
// allocate memory for the buffer in the GPU
glBufferData(GL_UNIFORM_BUFFER, sizeof(skelShader), NULL, GL_STATIC_DRAW);
// because we hard-coded "binding=3" in the shader code we can do this:
// bind Uniform Buffer to binding point 3 (without caring about index of UBO)
glBindBufferBase(GL_UNIFORM_BUFFER, 4, sksBuff);
// good practice, unbind buffer
glBindBuffer(GL_UNIFORM_BUFFER, 0);
sksBuff is just an GLuint.
I fill this array with new values every render/frame that goes by, these values are the new transformations for the joints. I do it like this:
for (int i = 0; i < skeleton.size(); i++) {
globalSkelInfo.currentJointTrans[i] = skeleton[i]->transformMat[currentFrame - 1] * skeleton[i]->globalBindPosMat;
}
This is working correctly for the root joint, but the rest of the joints/mesh remains in bind pose. The problem should be located in where I update the array. Currently I do it like this in the render function after I've done the multiplication for each joint:
for (int i = 0; i < skeleton.size(); i++) {
glUniformMatrix4fv(glGetUniformLocation(aShaderProgram, ("currentJointTrans[" + std::to_string(i) + "]").c_str()),
1, GL_FALSE, glm::value_ptr(globalSkelInfo.currentJointTrans[i]));
}
After this I draw. The root joints values seem to be moving correctly, but the rest of the mesh is in bindpose and doesn't move. In the Vertex Shader I try to update the matrix like this:
#version 440
const int maxJoints = 100;
const int maxWeights = 4;
layout(location = 0) in vec3 vertex_position;
layout(location = 1) in vec2 vertex_UV;
layout(location = 2) in vec3 vertex_normal;
layout(location = 3) in vec4 vertex_weight;
layout(location = 4) in ivec4 vertex_controllers;
out vec2 outUVs;
out vec3 outNorm;
layout(binding = 3 , std140) uniform uniformBlock
{
vec3 camPos;
mat4 world;
mat4 LookAt;
mat4 projection;
mat4 MVP;
};
layout(binding = 4 , std140) uniform animationStruct
{
mat4 currentJointTrans[maxJoints];
};
void main() {
vec4 finalModelPos = vec4(0.0);
vec4 finalNormal = vec4(0.0);
for (int i = 0; i < 4; i++) {
mat4 jointTrans = currentJointTrans[vertex_controllers[i]];
vec4 posePos = jointTrans * vec4(vertex_position, 1.0);
finalModelPos += posePos * vertex_weight[i];
vec4 worldNormal = jointTrans * vec4(vertex_normal, 0.0);
finalNormal += worldNormal * vertex_weight[i];
}
gl_Position = MVP * finalModelPos;
outNorm = finalNormal.xyz;
outUVs = vertex_UV;
}
My theory is that the updating of the struct skelShader with my currentJointTrans array is incorrect. Any tips on how I should do this instead?
glUniform* calls cannot set data in uniform buffers. Indeed, the whole point of uniform buffers is that the uniform data comes from a buffer object. That's why you had to create one.
So if you want to set the uniform data for a uniform block, you set that data into the buffer object.
tl;dr:
What is the best method for accessing data from each individual vertex whilst in the fragment shader?
e.g.
The triangle in this fragment is made up from vertices v0,v1 and v2 and I want to give each vertex a specific integer which I can use to pick a texture and then fade between the 3 in the fragment shader; I don't want these ids to be interpolated and it is important that I can access each vertex's id.
Current Situation:
I am currently writing a shader for rendering terrain; I have a function in my fragment shader which will return the appropriate texture colour from uvs for a given ID (By means of a texture atlas). I can then fade between the 3 textures to give the give smoothly textured terrain
Current Code
Vertex Shader:
#version 330 core
layout(location = 0) in vec3 in_position;
layout(location = 1) in vec2 in_uv_coords;
layout(location = 2) in vec3 in_normal;
layout(location = 3) in float in_texture_id;
uniform mat4 view_matrix;
uniform mat4 projection_matrix;
out vec2 pass_uv_coords;
out vec3 pass_normal;
out vec3 texture_ratio;
out float pass_fake_brightness;
out float pass_id0;
out float pass_id1;
out float pass_id2;
void CalculateFakeLighting()
{
const vec3 light_direction = normalize(vec3(1,-1,1));
vec3 unit_normal = normalize(in_normal);
float normal_dot_light = dot(unit_normal, -light_direction);
pass_fake_brightness = max(0.2, normal_dot_light);
}
void main()
{
pass_uv_coords = in_uv_coords;
pass_normal = in_normal;
gl_Position = projection_matrix * view_matrix * vec4(in_position, 1.0);
int tile_track = int(mod(gl_VertexID, 3));
switch(tile_track)
{
case 0:
texture_ratio = vec3(1,0,0);
pass_id0 = in_texture_id;
break;
case 1:
texture_ratio = vec3(0,1,0);
pass_id1 = in_texture_id;
break;
case 2:
texture_ratio = vec3(0,0,1);
pass_id0 = in_texture_id;
break;
};
CalculateFakeLighting();
}
Fragment Shader:
#version 330 core
in vec2 pass_uv_coords;
in vec3 pass_normal;
in vec3 texture_ratio;
in float pass_fake_brightness;
in float pass_id0;
in float pass_id1;
in float pass_id2;
const int HORIZONTAL_IDS = 8;
const int VERTICAL_IDS = 8;
uniform sampler2D texture0_sampler;
out vec4 colour;
void UseFakeLighting()
{
colour *= pass_fake_brightness;
}
vec2 CorrectUVs(vec2 uvs)
{
vec2 corrected_uvs = uvs;
const float cushion = 0.001;
//Correct UV scale
while(corrected_uvs.x >= 1)
corrected_uvs.x--;
while(corrected_uvs.y >= 1)
corrected_uvs.y--;
if(corrected_uvs.x < cushion)
corrected_uvs.x = cushion;
if(corrected_uvs.x > 1 - cushion)
corrected_uvs.x = 1 - cushion;
if(corrected_uvs.y < cushion)
corrected_uvs.y = cushion;
if(corrected_uvs.y > 1 - cushion)
corrected_uvs.y = 1 - cushion;
return corrected_uvs;
}
vec4 GetTexture(float id, vec2 uv_coords)
{
vec2 step = vec2(
1.0/HORIZONTAL_IDS,
1.0/VERTICAL_IDS
);
uv_coords.x/=HORIZONTAL_IDS;
uv_coords.y/=VERTICAL_IDS;
uv_coords.x += step.x * mod(id, HORIZONTAL_IDS);
uv_coords.y += step.y * floor(id/VERTICAL_IDS);
//Texture is upsidedown
uv_coords.y = 1.0 - uv_coords.y;
return texture(texture0_sampler, uv_coords);
}
void main()
{
vec2 corrected_uvs = CorrectUVs(pass_uv_coords);
vec3 correct_ratio = normalize(texture_ratio);
colour = GetTexture(pass_id0, corrected_uvs) * correct_ratio.x +
GetTexture(pass_id1, corrected_uvs) * correct_ratio.y +
GetTexture(pass_id2, corrected_uvs) * correct_ratio.z;
if(colour.a == 0)
discard;
UseFakeLighting();
}
By default the output variables from the vertex shader to the fragment shader use perspective-correct interpolation. If you want no interpolation done then qualify your variables with flat:
flat out vec3 pass_id0;
For more info see GLSL Type Qualifiers. Also see this question “flat” qualifier in glsl?
As recommended by #aslg and #AndonM.Coleman, geometry is a good solution to this issue. A flat vec3 is passed out of the geometry stage, which stores the id of each vertex which is then accessible in the fragment shader.
The key lines are in the geometry shader; one part of the output is
flat vec3 texture_ids;
Which is then set as such:
vertex_out.texture_ids.x = vertex_in[0].texture_id;
vertex_out.texture_ids.y = vertex_in[1].texture_id;
vertex_out.texture_ids.z = vertex_in[2].texture_id;
Full Shader Sources:
Vertex
#version 330 core
layout(location = 0) in vec3 in_position;
layout(location = 1) in vec2 in_uv_coords;
layout(location = 2) in vec3 in_normal;
layout(location = 3) in float in_texture_id;
out VertexData
{
vec2 uv_coord;
vec3 normal;
uint texture_id;
} vertex_out;
void main()
{
vertex_out.uv_coord = in_uv_coords;
vertex_out.normal = in_normal;
vertex_out.texture_id = uint(round(in_texture_id));
gl_Position = vec4(in_position, 1.0);
}
Geometry
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
uniform mat4 view_matrix;
uniform mat4 projection_matrix;
mat4 vp_matrix = projection_matrix * view_matrix;
in VertexData
{
vec2 uv_coord;
vec3 normal;
uint texture_id;
} vertex_in[];
out VertexDataPass
{
vec2 uv_coord;
vec3 normal;
vec3 texture_ratio;
float brightness;
flat vec3 texture_ids;
} vertex_out;
void CalculateFakeBrightness(int index)
{
const vec3 light_direction = normalize(vec3(1,-1,1));
vec3 unit_normal = normalize(vertex_in[index].normal);
float normal_dot_light = dot(unit_normal, -light_direction);
vertex_out.brightness = max(0.2, normal_dot_light);
}
void main()
{
for(int i = 0; i < gl_in.length(); i++)
{
gl_Position = vp_matrix * gl_in[i].gl_Position;
vertex_out.uv_coord = vertex_in[i].uv_coord;
vertex_out.normal = vertex_in[i].normal;
vertex_out.texture_ids.x = vertex_in[0].texture_id;
vertex_out.texture_ids.y = vertex_in[1].texture_id;
vertex_out.texture_ids.z = vertex_in[2].texture_id;
CalculateFakeBrightness(i);
switch(int(mod(i,3)))
{
case 0:
vertex_out.texture_ratio = vec3(1,0,0);
break;
case 1:
vertex_out.texture_ratio = vec3(0,1,0);
break;
case 2:
vertex_out.texture_ratio = vec3(0,0,1);
break;
};
EmitVertex();
}
EndPrimitive();
}
Fragment
#version 330 core
in VertexDataPass
{
vec2 uv_coord;
vec3 normal;
vec3 texture_ratio;
float brightness;
flat vec3 texture_ids;
} vertex_data;
const int HORIZONTAL_IDS = 8;
const int VERTICAL_IDS = 8;
uniform sampler2D texture0_sampler;
out vec4 colour;
vec2 CorrectUVs(vec2 uvs)
{
vec2 corrected_uvs = uvs;
const float cushion = 0.001;
//Correct UV scale
while(corrected_uvs.x >= 1)
corrected_uvs.x--;
while(corrected_uvs.y >= 1)
corrected_uvs.y--;
if(corrected_uvs.x < cushion)
corrected_uvs.x = cushion;
if(corrected_uvs.x > 1 - cushion)
corrected_uvs.x = 1 - cushion;
if(corrected_uvs.y < cushion)
corrected_uvs.y = cushion;
if(corrected_uvs.y > 1 - cushion)
corrected_uvs.y = 1 - cushion;
return corrected_uvs;
}
vec4 GetTexture(uint id, vec2 uv_coords)
{
vec2 step = vec2(
1.0/HORIZONTAL_IDS,
1.0/VERTICAL_IDS
);
uv_coords.x/=HORIZONTAL_IDS;
uv_coords.y/=VERTICAL_IDS;
uv_coords.x += step.x * mod(id, HORIZONTAL_IDS);
uv_coords.y += step.y * floor(float(id)/VERTICAL_IDS);
//Texture is upsidedown
uv_coords.y = 1.0 - uv_coords.y;
return texture(texture0_sampler, uv_coords);
}
void main()
{
vec2 uvs = CorrectUVs(vertex_data.uv_coord);
colour =
GetTexture(uint(vertex_data.texture_ids.x), uvs) * vertex_data.texture_ratio.x +
GetTexture(uint(vertex_data.texture_ids.y), uvs) * vertex_data.texture_ratio.y +
GetTexture(uint(vertex_data.texture_ids.z), uvs) * vertex_data.texture_ratio.z;
if(colour.a == 0)
discard;
colour.xyz *= vertex_data.brightness;
}