I want to make a program that shows the earth with a space texture as the background.
The earth is a 3D Uniform with a earth texture (.bmp).
The space with the stars is a texture (.bmp).
I have summarized what I have to do:
Create a new Model Matrix
Position it at the same place where the camera is
Disable depth test before drawing
Reverse culling
This is the Load function:
void load(){
//Load The Shader
Shader simpleShader("src/shader.vert", "src/shader.frag");
g_simpleShader = simpleShader.program;
// Create the VAO where we store all geometry (stored in g_Vao)
g_Vao = gl_createAndBindVAO();
//Create vertex buffer for positions, colors, and indices, and bind them to shader
gl_createAndBindAttribute(&(shapes[0].mesh.positions[0]), shapes[0].mesh.positions.size() * sizeof(float), g_simpleShader, "a_vertex", 3);
gl_createIndexBuffer(&(shapes[0].mesh.indices[0]), shapes[0].mesh.indices.size() * sizeof(unsigned int));
gl_createAndBindAttribute(uvs, uvs_size, g_simpleShader, "a_uv", 2);
gl_createAndBindAttribute(normal, normal_size, g_simpleShader, "a_normal", 2);
//Unbind Everything
gl_unbindVAO();
//Store Number of Triangles (use in draw())
g_NumTriangles = shapes[0].mesh.indices.size() / 3;
//Paths of the earth and space textures
Image* image = loadBMP("assets/earthmap1k.bmp");
Image* space = loadBMP("assets/milkyway.bmp");
//Generate Textures
glGenTextures(1, &texture_id);
glGenTextures(1, &texture_id2);
//Bind Textures
glBindTexture(GL_TEXTURE_2D, texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//We assign your corresponding data
glTexImage2D(GL_TEXTURE_2D,1,GL_RGB,image->width, image->height,GL_RGB,GL_UNSIGNED_BYTE,image->pixels);
glTexImage2D(GL_TEXTURE_2D,1,GL_RGB,space->width, space->height,GL_RGB,GL_UNSIGNED_BYTE,space->pixels);
}
This is the Draw function:
void draw(){
//1. Enable/Disable
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
//2. Shader Activation
glUseProgram(g_simpleShader);
//3. Get All Uniform Locations
//Space:
GLuint model_loc2 = glGetUniformLocation (g_simpleShader, "u_model");
GLuint u_texture2 = glGetUniformLocation(g_simpleShader, "u_texture2");
GLuint u_light_dir2 = glGetUniformLocation(g_simpleShader,"u_light_dir2");
//Earth
GLuint model_loc = glGetUniformLocation(g_simpleShader, "u_model");
GLuint projection_loc = glGetUniformLocation(g_simpleShader, "u_projection");
GLuint view_loc = glGetUniformLocation(g_simpleShader, "u_view");
GLuint u_texture = glGetUniformLocation(g_simpleShader, "u_texture");
GLuint u_light_dir = glGetUniformLocation(g_simpleShader, "u_light_dir");
//4. Get Values From All Uniforms
mat4 model_matrix2 = translate(mat4(1.0f), vec3(1.0f,-3.0f,1.0f));
mat4 model_matrix = translate(mat4(1.0f),vec3(0.0f,-0.35f,0.0f);
mat4 projection_matrix = perspective(60.0f,1.0f,0.1f,50.0f);
mat4 view_matrix = lookAt(vec3( 1.0f, -3.0f, 1.0f),vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f)glm::vec3(0,1,0));
//5. Upload Uniforms To Shader
glUniformMatrix4fv(model_loc2, 1, GL_FALSE, glm::value_ptr(model_matrix2));
glUniformMatrix4fv(model_loc, 1, GL_FALSE, glm::value_ptr(model_matrix));
glUniformMatrix4fv(projection_loc, 1, GL_FALSE, glm::value_ptr(projection_matrix));
glUniformMatrix4fv(view_loc, 1, GL_FALSE, glm::value_ptr(view_matrix));
glUniform1i(u_texture, 0);
glUniform3f(u_light_dir, g_light_dir.x, g_light_dir.y, g_light_dir.z);
glUniform1i(u_texture2, 1);
glUniform3f(u_light_dir2, g_light_dir.x, g_light_dir.y, g_light_dir.z);
//6. Activate Texture Unit 0 and Bind our Texture Object
glActiveTexture(GL_TEXTURE0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id2);
//7. Bind VAO
gl_bindVAO(g_Vao);
//8. Draw Elements
glDrawElements(GL_TRIANGLES, 3 * g_NumTriangles, GL_UNSIGNED_INT, 0);
}
Also I have two Fragment Shaders:
The first one returns this:
fragColor = vec4(final_color, 1.0);
The second one returns this:
fragColor = vec4(texture_color.xyz, 1.0);
Also the Vertex Shader returns the position of the vertex:
gl_Position = u_projection * u_view * u_model * vec4( a_vertex , 1.0 );
When I compile, it only shows the earth while it should show the earth and the space as background. I have reviewed the code several times but I can not find out what it is.
Suposed result:
My Result
If I see it right among other things you are wrongly binding textures
glActiveTexture(GL_TEXTURE0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id2);
should be:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_id);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_id2);
but I prefer that last set active units is 0 ...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_id2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_id);
that will save you a lot of troubles when you start combine code with single texture unit code ... Also hope you are properly unbinding the used texture units for the same reasons...
You got ugly seam on the edge 0/360deg of longitude
this might be caused by wrongly computed normal for lighting, wrong not seamless texture or just by forgeting to duplicate the edge points with correct texture coordinates for the last patch. See:
Applying map of the earth texture a Sphere
You can also add atmosphere,bump map, clouds to your planet:
Bump-map a sphere with a texture map
Andrea is right...
set matrices as unit matrix and render (+/-)1.0 rectangle at z=0.0 +/- aspect ratio correction without depth test, face culling and depth write ... That way you will avoid jitter and flickering stuff due to floating point errors.
Skybox is better but there are also other options to enhance
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
and all the sublinks in there especially stars. You can combine skybox and stellar catalog together and much much more...
Related
I just started learning opengl technology.
My program draw 2d isometric tiles and program output this:
Be unknown reasons black lines appear when two textures overlap or two textures touch.
Code example:
typedef unsigned int ID;
class GraphicEngine {
public:
GraphicEngine();
~GraphicEngine();
void initShaders(const char* vertexShaderSource, const char* fragmentShaderSource);
void initRenderData(float vertices[], unsigned int size);
std::vector<ID> initTextures(std::vector<std::string>& paths);
void drawTextures(std::vector<ID> testuresIds);
private:
GraphicEngine(GraphicEngine&) = delete;
GraphicEngine(GraphicEngine&&) = delete;
GraphicEngine& operator=(const GraphicEngine& other) = delete;
private:
unsigned int VBO = 0;
unsigned int VAO = 0;
unsigned int EBO = 0;
unsigned int shaderProgram;
};
GraphicEngine::GraphicEngine() {
}
GraphicEngine::~GraphicEngine() {
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
}
void GraphicEngine::initShaders(const char* vertexShaderSource, const char* fragmentShaderSource) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
shaderProgram = glCreateProgram();
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
}
void GraphicEngine::initRenderData(float vertices[], unsigned int size) {
unsigned int indices[] = {
0, 1, 3,
1, 2, 3
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
}
std::vector<ID> GraphicEngine::initTextures(std::vector<std::string>& paths) {
std::vector<ID> ids(paths.size());
stbi_set_flip_vertically_on_load(true);
for (int i = 0; i < paths.size(); i++) {
unsigned int texture;
glGenTextures(1, &ids[i]);
glBindTexture(GL_TEXTURE_2D, ids[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int width, height, nrChannels;
unsigned char* data = stbi_load(paths[i].c_str(), &width, &height, &nrChannels, STBI_rgb_alpha);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
stbi_image_free(data);
}
return ids;
}
void GraphicEngine::drawTextures(std::vector<ID> testuresIds) {
static bool ex = false;
for (auto testureId : testuresIds) {
for (int i = 0; i < 4; i++) {
glBindTexture(GL_TEXTURE_2D, testureId);
glm::mat4 transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(i * 0.6f + 0.0f, 0.0f, 0.0f));
glUseProgram(shaderProgram);
unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
for (int i = 0; i < 4; i++) {
glBindTexture(GL_TEXTURE_2D, testureId);
glm::mat4 transform = glm::mat4(1.0f);
transform = glm::translate(transform, glm::vec3(i * 0.6f - 0.3f, -0.16f, 0.0f));
glUseProgram(shaderProgram);
unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
}
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
Window::Window():window(nullptr) {}
Window::~Window() {
glfwTerminate();
}
bool Window::initWindowResources() {
bool result = false;
if (glfwInit() == GLFW_TRUE) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window != nullptr) {
glfwMakeContextCurrent(window);
if (glfwSetFramebufferSizeCallback(window, [](GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height); }) == NULL) {
if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
result = true;
}
}
}
}
return result;
}
const char* vertexShaderSource =
"#version 330 core\n"
"layout(location = 0) in vec3 aPos;\n"
"layout(location = 1) in vec2 aTexCoord;\n"
"out vec2 TexCoord;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
" gl_Position = transform * vec4(aPos, 1.0);\n"
" TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}\n\0";
const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, TexCoord);\n"
"}\n\0";
void Window::mainWindowLoop() {
graphicEngine.initShaders(vertexShaderSource, fragmentShaderSource);
std::vector<std::string> pathsTextures = { "C:\\Users\\Олег\\\Desktop\\sea1.png" };
float vertices[] = {
// positions // colors // texture coords
-1.3f, 0.16f, 0.0f, 1.0f, 1.0f, // top right
-1.3f, -0.16f, 0.0f, 1.0f, 0.0f, // bottom right
-0.7f, -0.16f, 0.0f, 0.0f, 0.0f, // bottom left
-0.7f, 0.16f, 0.0f, 0.0f, 1.0f // top left
};
graphicEngine.initRenderData(vertices, sizeof(vertices));
std::vector<ID> idsTextures = graphicEngine.initTextures(pathsTextures);
while (!glfwWindowShouldClose(window))
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
graphicEngine.drawTextures(idsTextures);
glfwSwapBuffers(window);
glfwPollEvents();
}
}
int main()
{
Window window;
if (window.initWindowResources()) {
window.mainWindowLoop();
}
return 0;
}
Png: Size png: 62x34 pixels, Transparent sprite, use prog to created png: piskelapp
Please, pvoide information about this issue: inforamtion about reasons of this issue and how to fix this issue.
I was able to reproduce your issue. You are working with non-premultiplied alpha, this is known for producing undesirable results when rendering translucent images.
Take a look at this article: http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/
Now, to solve your problem, first change your blend function to glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA).
Second, stbi doesn't pre-multiply the alpha on load, you have to do it manually.
Each pixel is composed by 4 bytes, red, green, blue and alpha, on the 0-255 range. Convert each value to the normalized range (0.0f - 1.0f) by dividing by 255.0f, multiply r, g, and b by alpha, then multiply it back by 255.0f;
The dark lines at the edge of the tiles are results of alpha blending and texture filtering.
The linked tile image (PNG) contains three premultipled color channels (red, green, blue) and transparency information (alpha channel) with no partially transparent pixels (the alpha value is either 1.0 or 0.0 everywhere, which results in sharp edges):
This can be checked in an image editor (for example Gimp). The image uses premultiplied alpha, i.e. the color channels were masked by the alpha channel and only contain color information where the alpha channel is non-zero.
The area outside of the valid image region is all black, so when OpenGL uses linear texture interpolation (GL_LINEAR) it will mix the hidden black texels right at the edge with the visible colored texels, which can result in a dark color, depending on the used blending function.
Alpha blending mixes the already present color in the framebuffer (of the cleared background or the already written fragments) with the incoming ones.
The used blending function glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) instructs the hardware to do this for every pixel:
The result: dark artifacts at the edges of each tile caused by the interpolated alpha value at the edges of the tile, which darkens the source color (sRGB * sA) (modified example with original tile image, reproduced issue from the original post):
In other words:
https://shawnhargreaves.com/blog/texture-filtering-alpha-cutouts.html:
Texture filtering: alpha cutouts
(...)
Filtering applies equally to the RGB and alpha channels. When used on the alpha
channel of a cutout texture it will produce new fractional alpha
values around the edge of the shape, which makes things look nice and
antialiased. But filtering also produces new RGB colors, part way in
between the RGB of the solid and transparent parts of the texture.
Thus the RGB values of supposedly transparent pixels can bleed into
our final image.
This most often results in dark borders around alpha cutouts, since
the RGB of transparent pixels is often black. Depending on the
texture, the bleeding could alternatively be white, pink, etc.
To quick-fix the problem, the blending function could simply by changed to glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA), since the tile image already has premultiplied RGB channels, which are all black (= 0) in transparent areas:
https://shawnhargreaves.com/blog/premultiplied-alpha.html:
Premultiplied alpha is better than conventional blending for several
reasons:
It works properly when filtering alpha cutouts (...)
It works properly when doing image composition (...)
It is a superset of both conventional and additive blending. If you set alpha to zero while RGB is non zero, you get an additive
blend. This can be handy for particle systems that want to smoothly
transition from additive glowing sparks to dark pieces of soot as the
particles age.
The result: dark artifacts disappear almost entirely after changing the blending function (modified example with original tile image, issue partially fixed):
Not perfect.
To fix this, some pixels could be drawn around the tile to enlarge the visible area a bit:
To let tiles overlap a bit, like that:
The result (with texture filtering, and overlapped pixels):
(Additionally, lines/other graphical elements could be drawn on top of the artifacts to cover them up. And if the pixelated jagged edges are not wanted, the actual textured polygons quads could be replaced by rhombuses that could be placed precisely next to each other in a continuous mesh that could be rendered in one draw call, no alpha blending required anymore, however sharp edges do not fit a pixelated look I guess.)
A possible solution using GL_NEAREST:
OpenGL texture parameters:
To get rid of the artefacts and blurred/filtered look, GL_LINEAR can be replaced by GL_NEAREST, which disables texture interpolation altogether for the selected texture and lets OpenGL render the raw pixels without applying texture filtering (GL_CLAMP_TO_EDGE makes sense here to avoid artifacts at the edges):
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Power of Two Textures:
OpenGL performance can be improved by always using texture dimensions that
are a power of two, e.g. 64x32 (instead of 60x32 in your case). - The tile image could be modified, e.g.: 2 pixels added on each side (and borders marked):
Side note: This restriction is not that important anymore, but in the past it was even necessary to use a special extension to enable NPOT textures:
Conventional OpenGL texturing is limited to images with
power-of-two dimensions and an optional 1-texel border.
ARB_texture_non_power_of_two extension relaxes the size restrictions
for the 1D, 2D, cube map, and 3D texture targets.
Snap to pixel:
There are multiple ways to do this with OpenGL.
I would recommend to scale the orthographic projection, so that 1 OpenGL
coordinate unit exactly matches 1 texel unit. That way, tiles can be precisely placed on the pixel grid (just shift coordinates of the tile vertices by 64 pixels/OpenGL units left/right, to get to the next one, in this example). Coordinates could be represented as integers in the engine now.
Modified code example:
void GraphicEngine::drawTextures(std::vector<ID> testuresIds, float wndRatio) {
const int countx = 3, county = 3; /* number of tiles */
const float scale = 100.0f; /* zoom */
const glm::mat4 mvp = glm::ortho(-wndRatio * scale, wndRatio * scale, -scale, scale, 2.0f, -2.0f);
const float offx = -((countx * TILE_WIDTH * 2.0f) * 0.5f - TILE_WIDTH);
const float offy = -TILE_WIDTH * 0.5f;
for (auto testureId : testuresIds) {
for (int y = 0; y < county; y++) {
for (int x = 0; x < countx - (y & 1 ? 1 : 0); x++) {
const glm::mat4 transform = mvp * glm::translate(glm::mat4(1.0f), glm::vec3(
offx + x * TILE_WIDTH * 2.0f + (y & 1 ? TILE_WIDTH : 0.0f),
offy + y * TILE_HEIGHT, 0.0f));
glBindTexture(GL_TEXTURE_2D, testureId);
const GLint transformLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
}
}
}
Screenshot of modified example:
And without marked edges:
Some hints on the use of "straight alpha textures":
Another approach to solve this might be the use of an unmasked/unpremultiplied/straight alpha texture. The color channels of the original tile image can be flood filled out like this:
(Note: The linked PNG image above can't be used directly. Imgur seems to convert transparent PNG images and automatically masks the color channels...)
This technique could help to reduce the artifacts when texture filtering and the conventional alpha blending function is used (i.e. GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). However, the background will always show through a tiny bit, because some pixels are always sightly transparent at the edges (caused by texture filtering):
(The result should be very similar to the first solution above, where the original premultiplied image is used with modified alpha blending (GL_ONE, GL_ONE_MINUS_SRC_ALPHA).)
If the tile contains not just a plain color, the color information at the edges of the tile would need to be extended outwards to avoid artifacts.
Obviously this doesn't solve the original issue completely, when a precise 2D look is the goal. But it could be useful in other situations, where the hidden pixels also generate bad results when other forms of transparency/blending/compositing are used, e.g. for particles, semi-transparent edges of foliage, text etc.
Some general hints that could help to solve the issue:
Check: glBlendFunc(), glBlendFuncSeparate(), glBlendEquation(), glBlendColor()
Do not waste precious video memory: glGenerateMipmap() is not required for a pure 2D presentation, where all pixels are visible all the time.
Alpha Testing: glAlphaFunc() has been removed from OpenGL 4, but alpha testing can be done manually in the fragment shader, just discard fragments depending on the alpha value, see OpenGL alpha test - How to replace AlphaFunc deprecated?.
glHint(): Using this OpenGL function to change implementation-specific hints can have an impact on the rendered result, sometimes in "surprising ways":
GL_POLYGON_SMOOTH_HINT
Indicates the sampling quality of antialiased polygons. Hinting
GL_NICEST can result in more pixel fragments being generated during
rasterization, if a larger filter function is applied.
The code in the original question comes with some issues, it does not compile like that (some parts of the class definitions are missing etc.). It takes some effort to reproduce to issue, which makes it more complicated to answer the question. - And it wasn't completely clear to me whether the intention is to just render seamless, pixelated tiles (solution: use GL_NEAREST), or if texture filtering is required...
Here my modified code example.
Related questions on Stack Overflow:
OpenGL normal blending with black alpha edges
opengl es2 premultiplied vs straight alpha + blending
Some links related to Alpha Blending / "premultiplied alpha":
Visual glBlendFunc + glBlendEquation Tool, by Anders Riggelsen
Premultiplied alpha, originally posted to Shawn Hargreaves Blog on MSDN, Friday, November 6, 2009
Texture filtering: alpha cutouts, originally posted to Shawn Hargreaves Blog on MSDN, Monday, November 2, 2009
What is Premultiplied Alpha? A Primer for Artists, by David Hart, July 6, 2016
GPUs prefer premultiplication, by Eric from www.realtimerendering.com
I have attached two textures to an FBO. The first texture will be used to show a depth map. The second texture will show the object in a normal way.
If I do this, it works well and shows me the depth map.
GLuint atach0 = GL_DEPTH_ATTACHMENT;
glBindFramebuffer(GL_FRAMEBUFFER,fboBuffer);
glDrawBuffers(1,&atach0);
glClear(GL_DEPTH_BUFFER_BIT);
glViewport(0.0,0.0,640,480);
LProjection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,-500.0f,500.0f);
LView = glm::lookAt(glm::vec3(0.0f,15.0f,0.000001f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f));
LViewProjection = LProjection * LView;
glEnable(GL_DEPTH_TEST);
...CUBE3D
glBindFramebuffer(GL_FRAMEBUFFER,0);
glUniform1i(uniforTEX,0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,GL_DEPTH_MAP);
...DRAW-DEPTH-MAP!
glBindTexture(GL_TEXTURE_2D,0);
But if I do this, it only shows a white screen and the depth map is no longer visible.
GLuint atach0 = GL_DEPTH_ATTACHMENT;
glBindFramebuffer(GL_FRAMEBUFFER,fboBuffer);
glDrawBuffers(1,&atach0);
glClear(GL_DEPTH_BUFFER_BIT);
glViewport(auxrecX,auxrecY,auxrcAn,auxrcAl);
LProjection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,-500.0f,500.0f);
LView = glm::lookAt(glm::vec3(0.0f,15.0f,0.000001f),glm::vec3(0.0f,0.0f,0.0f),glm::vec3(0.0f,1.0f,0.0f));
LViewProjection = LProjection * LView;
glEnable(GL_DEPTH_TEST);
...CUBE3D
glBindFramebuffer(GL_FRAMEBUFFER,0);
GLuint atach1 = GL_COLOR_ATTACHMENT0;
glBindFramebuffer(GL_FRAMEBUFFER,fboBuffer);
glDrawBuffers(1,&atach1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0,0,640,480);
Projection = glm::perspective(45.0f,1.333333f,0.01f,1000.0f);
View = glm::lookAt(glm::vec3(0.0f,0.0f,3.0),glm::vec3(0.0f,0.0f,-15.0f),glm::vec3(0.0f,1.0f,0.0f));
ViewProjection = Projection * View;
glEnable(GL_DEPTH_TEST);
...CUBE3D
glBindFramebuffer(GL_FRAMEBUFFER,0);
glUniform1i(uniforTEX,0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,GL_DEPTH_MAP);
...DO NOT DRAW NOTHING
glBindTexture(GL_TEXTURE_2D,0);
I need to do it in that order. Because the first "pass" is for the use of the depth map in a shadow map. What am I doing wrong?
GL_DEPTH_ATTACHMENT is not valid for glDrawBuffers. glDrawBuffers of buffers into which outputs from the fragment shader data will be written.
When you do
GLuint atach0 = GL_DEPTH_ATTACHMENT;
glBindFramebuffer(GL_FRAMEBUFFER,fboBuffer);
then you'll get a GL_INVALID_ENUM error.
If you don't want to wirte to the depth map, then disable the depth test of use glDepthMask. The depth buffer attachment can't be switched on and off and it can't be uses somehow like a color buffer.
Further
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
will clear the depth buffer in any case.
I think there is a basic misunderstanding how the framebuffers for a shadow map have to be set up. You'll need a framebuffer for the shadow map only. This framebuffer has to have a depth buffer only.
You have to do mit somehow like this:
GLuint fboShadow; // framebuffer with depth buffer only (shadow map)
GLuint toShadowDepth; // texture which is the depth buffer of `fboShadow`
glBindFramebuffer(GL_FRAMEBUFFER, fboShadow);
GLuint atach0 = GL_NONE;
glDrawBuffers(1, &atach0);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(auxrecX, auxrecY, auxrcAn, auxrcAl);
// draw the shadow map to the depth buffer only
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(0, 0, 640, 480);
glUniform1i(uniforTEX, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, toShadowDepth);
// draw the geometry to the default framebuffer by using the depth map
I generate a PointCloud in my program, and now, I want to be able to click on a point in this point cloud rendered to my screen using OpenGL.
In order to do so, I used the trick of giving to each pixel in an offscreen render a colour based on its index in the VBO. I use the same camera for my offscreen render and my onscreen render so they move together, and when I click, I get values of my offscreen render to retrieve the position in the VBO to get the point I clicked on. This is the theory since when I click, I have only (0,0,0). I believe that means my FBO is not well renderer but I'm not sure whether it is that or if the problem comes from somewhere else...
So here are the steps. clicFBO is the FBO I'm using for offscreen render, and clicTextureColorBuf is the texture in which I write in the FBO
glGenFramebuffers(1, &clicFBO);
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
glGenTextures(1, &clicTextureColorBuf);
glBindTexture(GL_TEXTURE_2D, clicTextureColorBuf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, clicTextureColorBuf, 0);
GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, DrawBuffers);
After that, I wrote a shader that gives to each point the color of its index in the VBO...
std::vector<cv::Point3f> reconstruction3D; //Will contain the position of my points
std::vector<float> indicesPointsVBO; //Will contain the indexes of each point
for (int i = 0; i < pts3d.size(); ++i) {
reconstruction3D.push_back(pts3d[i].pt3d);
colors3D.push_back(pt_tmp);
indicesPointsVBO.push_back(((float)i / (float)pts3d.size() ));
}
GLuint clicVAO, clicVBO[2];
glGenVertexArrays(1, &clicVAO);
glGenBuffers(2, &clicVBO[0]);
glBindVertexArray(clicVAO);
glBindBuffer(GL_ARRAY_BUFFER, clicVBO[0]);
glBufferData(GL_ARRAY_BUFFER, reconstruction3D.size() * sizeof(cv::Point3f), &reconstruction3D[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glEnable(GL_PROGRAM_POINT_SIZE);
glBindBuffer(GL_ARRAY_BUFFER, clicVBO[1]);
glBufferData(GL_ARRAY_BUFFER, indicesPointsVBO.size() * sizeof(float), &indicesPointsVBO[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
and the vertex shader:
layout (location = 0) in vec3 pos;
layout (location = 1) in float col;
out float Col;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform int pointSize;
void main()
{
gl_PointSize = pointSize;
gl_Position = projection * view * model * vec4(pos, 1.0);
Col = col;
}
And the Fragment:
#version 330 core
layout(location = 0) out vec4 FragColor;
in float Col;
void main()
{
FragColor = vec4(Col, Col, Col ,1.0);
}
And this is how I render this texture:
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 1.0f, 100.0f);
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
clicShader.use();
glDisable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
clicShader.setMat4("projection", projection);
clicShader.setMat4("view", view);
model = glm::mat4();
clicShader.setMat4("model", model);
clicShader.setInt("pointSize", pointSize);
glBindVertexArray(clicVAO);
glDrawArrays(GL_POINTS, 0, (GLsizei)reconstruction3D.size());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And then, when I click, I Use this piece of Code:
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
glReadBuffer(GL_COLOR_ATTACHMENT0);
int width = 11, height = 11;
std::array<GLfloat, 363> arry{ 1 };
glReadPixels(Xpos - 5, Ypos - 5, width, height, GL_RGB, GL_UNSIGNED_BYTE, &arry);
for (int i = 0; i < 363; i+=3) { // It's 3 time the same number anyways for each number
std::cout << arry[i] << " "; // It gives me only 0's
}
std::cout << std::endl << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
I know the error might be really stupid but I still have some problems with how OpenGL works.
I put what I thought was necessary to understand the problem (without extending too much), but if you need more code, I can write it too.
I know this is not a question in which you can say Yes or No and it's more like debugging my program, but since I really don't find from where the problem comes from, I'm looking toward someone who can explain to me what I did wrong. I do not necessarily seek the solution itself, but clues that could help me understand where my error is ...
Using a framebuffer object FBO to store a "object identifier" is a cool method. But also want to see the objects, right? Then you must render also to the default frame buffer (let me call it "defFB", which is not a FBO).
Because you need to render to two different targets, you need one of these techniques:
Draw objects twice (e.g. with two glDrawArrays calls), one to the FBO and a second one to the defFB.
Draw to two FBO's images at once and later blit one of then (with colors) to the defFB.
For the first technique you may use a texture attached to a FBO (as you currently do). Or you can use a "Renderbuffer" and draw to it.
The second approach needs a second "out" in the fragment shader:
layout(location = 0) out vec3 color; //GL_COLOR_ATTACHMENT0
layout(location = 1) out vec3 objID; //GL_COLOR_ATTACHMENT1
and setting the two attachments with glDrawBuffers.
For the blit part, read this answer.
Note that both "out" have the same format, vec3 in this example.
A fail in your code is that you set a RGB texture format and also use this format at glReadPixels, but your "out" in the FS is vec4 instead of vec3.
More concerns are:
Check the completeness with glCheckFramebufferStatus
Using a "depth attachment" to the FBO may be needed, even it will not be used for reading.
Disabling the depth test will put all elements if the frame. Your point-picking will select the last drawn, not the nearest.
I found the problem.
There were 2 failures in my code :
The first one is that in OpenGL, there is an Y inversion between the image and the framebuffer. So in order to pick the good point, you have to flip Y using the size of the viewport : I did it like this :
GLint m_viewport[4];
glGetIntegerv(GL_VIEWPORT, m_viewport);
int YposTMP = m_viewport[3] - Ypos - 1;
The second one is the use of
glReadPixels(Xpos - 2, Ypos - 2, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]);, the 6th parameter must be GL_FLOAT since the datas i'm returning are float.
Thanks all!
Best regards,
R.S
Hi, I am trying to display two objects using OpenGL viz., 1) a rotating cube with a mix of two textures (a wooden crate pattern and a smiley) in the foreground and 2) rectangular plate with just one texture (dark grey wood) as a background. When I comment out the part of the code governing the display of rectangular plate, the rotating cube displays both the textures (wooden crate and smiley). Otherwise, the cube displays only the wooden crate texture and the dark grey wood texture is also displayed on the rectangular plate, i.e. the smiley texture disappears from the rotating cube. Please find the images 1) http://oi68.tinypic.com/2la4r3c.jpg (with the rectangular plate portion of code commented) and 2) http://i67.tinypic.com/9u9rpf.jpg (without the rectangular plate portion of code commented). The relavant portion of the code is pasted below
// Rotating Cube ===================================================
// Texture of wooden crate
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(glGetUniformLocation(ourShader_box.Program, "ourTexture1"), 0);
// Texture of a smiley
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glUniform1i(glGetUniformLocation(ourShader_box.Program, "ourTexture2"), 1);
// lets use the box shader for the cube
ourShader_box.Use();
// transformations for the rotating cube ---------------------------------
glm::mat4 model_box, model1, model2;
glm::mat4 view_box;
glm::mat4 perspective;
perspective = glm::perspective(45.0f, (GLfloat)width_screen/(GLfloat)height_screen, 0.1f, 200.0f);
model1 = glm::rotate(model_box, (GLfloat)glfwGetTime()*1.0f, glm::vec3(0.5f, 1.0f, 0.0f));
model2 = glm::rotate(model_box, (GLfloat)glfwGetTime()*1.0f, glm::vec3(0.0f, 1.0f, 0.5f));
model_box = model1 * model2;
view_box= glm::translate(view_box, glm::vec3(1.0f, 0.0f, -3.0f));
GLint modelLoc_box = glGetUniformLocation(ourShader_box.Program, "model");
GLint viewLoc_box = glGetUniformLocation(ourShader_box.Program, "view");
GLint projLoc_box = glGetUniformLocation(ourShader_box.Program, "perspective");
glUniformMatrix4fv(modelLoc_box, 1, GL_FALSE, glm::value_ptr(model_box));
glUniformMatrix4fv(viewLoc_box, 1, GL_FALSE, glm::value_ptr(view_box));
glUniformMatrix4fv(projLoc_box, 1, GL_FALSE, glm::value_ptr(perspective));
// --------------------------------------------------------------------
// Draw calls
glBindVertexArray(VAO_box);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
// Rectangular Plate =====================================================
// Background Shader
ourShader_bg.Use();
// Texture of dark grey wood
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, texture_wood);
glUniform1i(glGetUniformLocation(ourShader_bg.Program, "ourTexture3"), 2);
// Transformations -------------------------------------------
glm::mat4 model_bg;
glm::mat4 view_bg;
GLint modelLoc_bg = glGetUniformLocation(ourShader_bg.Program, "model");
GLint viewLoc_bg= glGetUniformLocation(ourShader_bg.Program, "view");
GLint projLoc_bg = glGetUniformLocation(ourShader_bg.Program, "perspective");
glUniformMatrix4fv(modelLoc_bg, 1, GL_FALSE, glm::value_ptr(model_bg));
glUniformMatrix4fv(viewLoc_bg, 1, GL_FALSE, glm::value_ptr(view_bg));
glUniformMatrix4fv(projLoc_bg, 1, GL_FALSE, glm::value_ptr(perspective));
// -----------------------------------------------------------
// Draw calls
glBindVertexArray(VAO_bg);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
// =================================================================
I have a two questions regarding this code.
Why is the smiley disappearing?
Is this how multiple objects are supposed to be rendered? I know OpenGL does not care about objects, it only cares about vertices, but in this case these are separate, disjoint objects. So, should I be organizing them as two VBO's bound to a single VAO or as separate VBO's each bound to two VAO's for each object? Or is the case such that, either way is fine - depends on coder's choice and elegance of code?
You are using the same shader, same matrices and you have the same geometry type for the two objects (triangles), so why set the shader twice ?
Did you try to;
Set shader
Bind buffer #1
Bind texture #1
Draw object #1
Bind buffer #2
Bind texture #2
Draw object #2
I am confused about shadow mapping. Here's what I've understood (folowing steps are not working:) )
How to get profit (please don't get confused about the code, it is roughly because I write on Java):
1. Create empty depthTexture (mine is 1024x1024) with parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE) and
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, GL_NULL)
2. Create FBO and attach that texture to it
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0)
3. Setup new projection and view matrix for lighting camera
I used the same as my main camera just with another coords, because scene looks better in it (I tried it out, so there's no problem... I guess ..).
4. Create new little shader to determinate gl_Position for FBO and modify the main shader with fancy stuff (bias matrix * lightCamera matrix * vertex, sampler2Dshadow and more)
5. Of course uniform everything and do something.
And now RENDER LOOP
1. Of course uniform everything and do something(again).
2. Bind FBO, bind little shader, glViewport(0, 0, 1024, 1024), colorMask to false, clear depth buffer, glCullFace(GL_FRONT), glBindTexture(GL_TEXTURE_2D, 0)
3. Render everything using only vertex position attribs completely for nothing
4. Unbind FBO, rebind program to main shader (that one with fancy stuff), glViewport, enable colorMask, glCullFace(GL_BACK)
5. Render the scene normally without even thinking about "How the hell main shader gonna get sampler2DShadow because we dont bind it, dont uniform it, and don't even touch it"
6. Watch countless glitches, bugs and pixel orgy
Actually I tried to uniform depthTexture to sampler, but I've got black screen only. And even if I dont render FBO, I get the same picture when I do that.
Can someone explain, what am I missing?
I feel like shader uses diffuse texture twice: as a diffuse texture and as a depthTexture, but I don't know how to give it that depthTexture.
here is a good link to read: http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/
and here: http://www.paulsprojects.net/tutorials/smt/smt.html (although the second link uses old fixed function opengl)
in general:
create one depth texture
attach this texture to FBO
bind FBO, setup proper viewport
render scene from light pos, save only depth values to your texture
unbind FBO and setup final scene vieport and camera position
render scene normally using shadow test (sampler2DShadow)
you can render your depth map always (in render loop) or only when light pos changes.
I do not know why you are rendering you scene normally twice... are you using Z-prepass, or something? just try the basic version I think.
my old code with shadow maps:
void RenderShadowMap() {
currDepth->Bind();
glClear(GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gLightCam.SetProjectionMatrix();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gLightCam.SetViewMatrix();
glColorMask(false, false, false, false);
glUseProgram(0); // draw without any shaders... just default depth
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(offFactor, offUnits);
SimpleScene(false); // floor does not cast shadow so do not render it
glDisable(GL_POLYGON_OFFSET_FILL);
glColorMask(true, true, true, true);
}
render scene:
// compose shadow matrix:
MATRIX4X4 bias(0.5f, 0.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f);
MATRIX4X4 *invCam = gSphericalCam.GetInvViewMatrix();
MATRIX4X4 smMat = (*gLightCam.GetViewProjMatrix()) * (*invCam);
gShaderProgramManager->GetProgram("shadow")->Use();
gShaderProgramManager->GetProgram("shadow")->SetMatrix("shadowMat", &smMat);
gShaderProgramManager->GetProgram("shadow")->SetBool("useShadow", currCam != &gLightCam && useShadow);
gShaderProgramManager->GetProgram("shadow")->SetFloat("shadowMapSize", (float)currDepth->GetWidth());
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
gTextureManager->Bind("default");
glActiveTexture(GL_TEXTURE1);
currDepth->BindDepthAsTexture();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
SimpleScene();