Difference between revisions of "Discussion5S16"
m (→All Put Together) |
|||
Line 63: | Line 63: | ||
glBindTexture(GL_TEXTURE_2D, textureID); | glBindTexture(GL_TEXTURE_2D, textureID); | ||
− | unsigned char * image = loadPPM("myTexture.ppm", | + | unsigned char * image = loadPPM("myTexture.ppm", width, height); |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); | ||
Latest revision as of 11:40, 29 April 2016
Contents |
Overview
Week 5 Discussion recap(04/25/16)
We had midterm review, and didn't actually go over the content below.
Crash Course on Textures in OpenGL
In terms of pure data type, OpenGL textures are actually quite simple to understand. They are a 2d array of pixel values(usually RGB, with each color being a byte) stored in the GPU's memory. The keyword that complicates things is that it's on the GPU's memory. Remember back to how VBOs, EBOs were complicated to handle because they lived in the GPU's memory? Well the same principle applies here. We need to use our OpenGL commands to manipulate those textures. But fear not! These commands will actually look very similar to the VBO commands.
Texture Generator
First up is the glGenTextures
function. This works very much like the glGenBuffers
function that was used in our buffer objects. This function needs to be called so we can generate a texture, or in other words tell the GPU to get ready because we're about to send a texture over.
GLuint textureID; glGenTextures(1, &textureID);
Now the texture's ID—as the GPU understands it—is stored in our variable textureID. This textureID will be used to refer to our texture from on. (Hint: Better store it somewhere it will last!)
I Got a Binder Full of Textures
Next we have to effectively turn on the texture. We do this with the glBindTexture
call. This is used for binding, as well as unbinding our texture to do operations on it.
glBindTexture(GL_TEXTURE_2D, textureID); /* Do some texture operations here ... */ glBindTexture(GL_TEXTURE_2D, 0);
The reason we need the textureID
is obvious, but what is that GL_TEXTURE_2D
doing there? Well, OpenGL has multiple types of textures it supports; the main texture we're interested here is a 2D texture, or in OpenGL enum: GL_TEXTURE_2D
. The final glBindTexture
code with the id set to 0 unbinds the texture, making sure our future texture commands can start at a clean state. Be responsible and keep things clean!
Load'em Up!
So what might we do in that code block in between? Well that depends on your goal: you might load the texture, you might draw your object, or you might configure the settings of your texture(clamp mode, interpolation mode, etc.). Let's see what an example of loading the texture might look like. For now, let's imagine you have a file myTexture.ppm
. Further, we're given a loadPPM
function that can read a PPM and return a 2D array of RGB values for us. Let's take a look at creating this 2D texture with the glTexImage2D
function:
unsigned char * image = loadPPM("myTexture.ppm", width, height); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
Simple right? No? :( Well let's go over each of the arguments into glTexImage2D.
-
GL_TEXTURE_2D
- Our target texture is a 2D texture and we're doing a 2D texture operation to it(giving it a flat image).
- The first
0
- This is the mipmap level we're sending data to. Since we're not interested in mipmapping here, we simply set this image as mip map level 0.
- The first
GL_RGB
- This is the internal representation of the texture once it's in our GPU's memory. Unless you're interested in having RGBA or representing depth, leave it as is.
-
width
- The width of the texture will be written to this variable
-
height
- Likewise.
- The second
0
- This parameter exists to be 0. Seriously. Don't ask me why. Consult the documentation if you're skeptical.
- The second
GL_RGB
- This is the representation of our texture as we're giving it to OpenGL. The
loadPPM
function we provide makes the image aGL_RGB
so best to comply with that.
- This is the representation of our texture as we're giving it to OpenGL. The
-
GL_UNSIGNED_BYTE
- This is the type of the individual values in our array. We're using
unsigned char
s inloadPPM
so we assign this toGL_UNSIGNED_BYTE
- This is the type of the individual values in our array. We're using
-
image
- The actual memory location of where our pixel data is stored.
All Put Together
GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); unsigned char * image = loadPPM("myTexture.ppm", width, height); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); glBindTexture(GL_TEXTURE_2D, 0);
Well, not so bad right? We only have to do this once, and as long as we remember our textureID
, we can use this texture by calling glBindTexture
again before we draw.
So now let's move on to using these textures!
Skybox
The skybox is literally what it sounds like, a box surrounding the sky. This is very useful for making your world and sky look enormous without actually creating that much geometry for it. Some interesting properties of the skybox are:
- It has 6 sides
- Duh! Well, this means we need 6 textures, when we've just barely learned how to do one! :O
- It will need it's own GLSL shaders, so we get to juggle multiple GLSL shaders.
- It will need to be scaled quite large. Maybe uniformly by 500.
So let's look at what this might look like.
Skybox Architecture
The skybox has enough unique features that we should probably encapsulate it in its own class. What do we need? Well we first need our geometry right? Hmm... I wonder where we've ever drawn a box-like object?
Leading questions aside, the skybox can have exactly the same vertices and faces data as the Cube.cpp
, and create VAO, VBO, and EBO objects based on them. Now the issue is that those values are too small to simulate a sky that is very very far away. A matrix that scales this cube will be very useful. Hmm... I truly wonder where which variable we've used to ever scaled objects uniformly?
Well, that takes care of geometry. What else do we need? The 6 textures of course! We'll address this concern in another section, so for now assume we have the textures loaded.
We have the geometry and textures, now we can draw our skybox. What does that bring to mind? Well a draw
method right? The skybox can be drawn much like how our cube or OBJObjects were drawn!
There's one caveat though right? We need a separate GLSL shader for our texture. The GLSL shaders are actually much simpler than Project 2 and we can simply take and modify the GLSL shaders from Learn OpenGL's Displaying a Skybox section.
How might we use a different shaderProgram
only for the skybox's draw method?
Leave it to OpenGL
We still have to address our textures right? Because cube maps are actually very commonly used, OpenGL provides us a useful texture type GL_TEXTURE_CUBE_MAP
that can contain 6 textures all by itself. This saves us from having to manage 6 different 2D textures manually, and also means we only need to keep track of one textureID
. When we bind a GL_TEXTURE_CUBE_MAP
, we get 6 additional types that we can access that we can call glTexImage2D
on: GL_TEXTURE_CUBE_MAP_POSITIVE_X
(right), GL_TEXTURE_CUBE_MAP_NEGATIVE_X
(left), GL_TEXTURE_CUBE_MAP_POSITIVE_Y
(up), GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
(down), GL_TEXTURE_CUBE_MAP_POSITIVE_Z
(back), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
(front).
So our loading call from Load'em up might expand into something like this:
unsigned char * image; image = loadPPM("left.ppm", width, height); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); image = loadPPM("right.ppm", width, height); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); image = loadPPM("up.ppm", width, height); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); image = loadPPM("down.ppm", width, height); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); image = loadPPM("back.ppm", width, height); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); image = loadPPM("front.ppm", width, height); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
Make sure you bind your texture as GL_TEXTURE_CUBE_MAP
type as well as when configuring your texture with glTextureParameteri
(as per the skybox section in project 3).