Update (11/5/2019)
As of writing this, the default KGL libraries for KOS are basically deprecated.
It's recommended to use Kazade's GLdc libraries over on GitHub.
To install Kazade's GLdc libraries:
1. Go to your kos/addons folder
2. type:
git clone https://github.com/Kazade/GLdc.git
3. type:
cd GLdc
4. type:
make defaultall
5. type:
make create_kos_link
Then, in your makefile, include "-lGLdc" instead of "-lGL".
Here's my makefile for reference:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: rm-elf main.elf | |
include $(KOS_BASE)/Makefile.rules | |
OBJS = main.o | |
clean: | |
-rm -f main.elf $(OBJS) | |
clean-all: | |
-rm -f main.elf $(OBJS) main.iso output.bin Program.cdi 1st_read.bin | |
dist: | |
-rm -f $(OBJS) | |
$(KOS_STRIP) main.elf | |
rm-elf: | |
-rm -f main.elf | |
main.elf: $(OBJS) | |
$(KOS_CC) $(KOS_CFLAGS) $(KOS_LDFLAGS) -o $@ $(KOS_START) $^ -lGLdc -lm $(KOS_LIBS) |
Note: Most of the functions from KGL carry over to GLdc, with one exception: glutSwapBuffers() is now glKosSwapBuffers().
I've already covered how one would draw a triangle in the PVR, now I'm going to show you how to draw a triangle using OpenGL (via GLdc).
Why OpenGL?

OpenGL is a relatively platform-independent graphics library that is almost universally used across a multitude of devices. It's relatively easy to use, and often forms the graphical basis for many cross-platform applications or libraries. The library provides a higher level way of communicating with the respective system's GPU.
Relevant Example Projects
- Anything in kos/addons/GLdc/samples
Specifically, the examples by Nehe are wonderful! - kos/examples/dreamcast/kgl
The Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <kos.h> | |
#include <GL\gl.h> | |
#include <GL\glu.h> | |
#include <GL\glut.h> | |
KOS_INIT_FLAGS(INIT_DEFAULT); | |
bool run = true; | |
int main(int argc, char** argv){ | |
// Controller input stuff. I already covered this in a previous tutorial. | |
maple_device_t* controller = maple_enum_type(0,MAPLE_FUNC_CONTROLLER); | |
cont_state_t* controllerState; | |
vid_set_mode(DM_640x480, PM_RGB565); | |
// Initialize OpenGL and the PVR | |
glKosInit(); | |
while(run){ | |
// Basic controller input. I already covered this in a previous tutorial. | |
if (controller){ | |
controllerState = (cont_state_t*) maple_dev_status(controller); | |
if (controllerState->buttons & CONT_A) | |
run = false; | |
} | |
glClear(GL_COLOR_BUFFER_BIT); // Every time we're drawing, we're clearing out the color buffer. | |
glBegin(GL_TRIANGLES); // This is where we tell OpenGL we're starting | |
// to draw something. We have to tell it what | |
// we're drawing. | |
// Bottom right | |
glColor3f(1.0f, 0.0f, 0.0f); | |
glVertex3f(-1.0f,-1.0f,1.0f); | |
// Top Middle | |
glColor3f(0.0f, 1.0f, 0.0f); | |
glVertex3f(0.0f,1.0f,1.0f); | |
// Bottom left | |
glColor3f(0.0f, 0.0f, 1.0f); | |
glVertex3f(1.0f,-1.0f,1.0f); | |
glEnd(); // Tell OpenGL that we're done drawing. | |
glKosSwapBuffers(); // Gotta swap the buffers otherwise we have issues! | |
} | |
return 0; | |
} |
And that's it! Almost half the size of our PVR-rendered triangle, and this is including extraneous input!
Keep in mind, a lot of the work involved with OpenGL is abstracted away behind the libraries. No need to setup everything manually (like most OpenGL tutorials will teach you how to do). You're not going to be an expert in OpenGL after learning how to draw this triangle, but regardless, getting anything to draw on the screen is a victory, in my book!
I commented the code, so I won't be breaking it down line-by-line, but I do want to highlight some key similarities and differences between working with the PVR, and OpenGL.
The Draw Loop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
glClear(GL_COLOR_BUFFER_BIT); // Every time we're drawing, we're clearing out the color buffer. | |
glBegin(GL_TRIANGLES); // This is where we tell OpenGL we're starting | |
// to draw something. We have to tell it what | |
// we're drawing. | |
// Bottom right | |
glColor3f(1.0f, 0.0f, 0.0f); | |
glVertex3f(-1.0f,-1.0f,1.0f); | |
// Top Middle | |
glColor3f(0.0f, 1.0f, 0.0f); | |
glVertex3f(0.0f,1.0f,1.0f); | |
// Bottom left | |
glColor3f(0.0f, 0.0f, 1.0f); | |
glVertex3f(1.0f,-1.0f,1.0f); | |
glEnd(); // Tell OpenGL that we're done drawing. | |
glutSwapBuffers(); // Gotta swap the buffers otherwise we have issues! |
The draw loop is pretty similar to the PVR.
First, we clear any of the buffer bits we need to clear. In this case, the only buffer bit we need to worry about is color. Then, we tell OpenGL we're about to draw something with glBegin(), telling it what kind of object we're planning on drawing. You can find these by looking at the OpenGL documentation.
After that, we're free to draw our vertices! Once we're done with that, we close it out with glEnd() and swap our video buffers with glKosSwapBuffers().
Submitting Vertices to Draw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Bottom right | |
glColor3f(1.0f, 0.0f, 0.0f); | |
glVertex3f(-1.0f,-1.0f,1.0f); |
One key difference, which frees up a significant chunk of code, is how OpenGL handles submitting vertices to be drawn. What was 4 lines of code for the PVR is chopped in half!

You might remember that from the PVR tutorial, vertices (allegedly) needed to be submitted clockwise. OpenGL doesn't really require that. However, it does require you to sort of loop back if you want to continue a new triangle. Think of it as if you had one continuous line that you're connecting between points.
We'll get more into this in the follow-up tutorial, where we actually draw a pyramid, but for now you don't need to worry about looping back.
Coordinates
OpenGL is a little funkier than the PVR, in that the screen actually goes from -1, to 1 (left and right sides of the screen respectively).
So, if our screen is 640x480 pixels, X=-1 in OpenGL would be the same as X=0 on the screen, and X=1 in OpenGL is would be the same as X=680 on the screen. X=0 in OpenGL would be smack-dab in the middle of c, at X=320.
It's a little confusing, but it makes sense if you think about it in a 3D space! Go to the left, and your X position will be less than 0. Go to the right, and your X position will be greater than 0.
If you're confused by this, try going into Unity or Unreal Engine and watch the coordinates of an object's position while you're moving it around!
And that's pretty much it! We have a triangle, just like the one we drew with the PVR! You'll notice we still don't have perspective, though! It's just a flat triangle on the screen. How do we fix that? In the next tutorial, I'll explain how to draw an object and display it in 3D!
If I did a poor job of explaining anything, please let me know! I'm a bit more familiar with this stuff, now, so I'm forgetting things that might be important to explain.
No comments:
Post a Comment