Introduction: SDL2 Raytracer Part 1 - Creating a Window

About: Hi! My name is Cory Hall. I am a Year 11 Computer Science student.

By the end of this instructable, you will have created a window using SDL2. We will use this in order to perform our raytracing algorithm and display the results.

Source code is available on GitHub.

Supplies

  • Visual Studio 2019 or later
  • Windows
  • A working knowledge of C++ and SDL2

Step 1: Create a New Project

To begin, we need to create a new project.


Open Visual studio, and create a new C++ console application.

Step 2: Add SDL2 to Project

Next, we will add SDL2 to the project. To do this, right click on the solution, then click "Manage nuGet packages".


Click on "Browse", search for "SDL2", and install the first result.

Step 3: Create the Window Class

Next, we need to create a class called "Window." This will hold all the information we need for SDL2, as well as the scene data for the raytracer.


Create a new class, and call it Window. Inside the header file, make sure to include "SDL.h"

Step 4: Define Window Data

Our window will hold several variables required for our window to work as intended. Let's go over a few of them now.

In the window class, add:

public:
  SDL_Window* win;
  SDL_Renderer* renderer;

  int width, height;
  const char* windowTitle;

So, what are these values for?

SDL_Window is a class used by SDL, and it contains all the data for a window, and also allows us to draw to it.

SDL_Renderer is linked to the window. We must interface with this renderer in order to display objects in the window.


Width and height, of course, are the width and height of the window, in pixels.

windowTitle is the title that will appear at the top of the window, next to the window icon. We won't worry about an icon for now, as it isn't necessary for the window to function.

Step 5: Window Constructor

In order to create a window, we will need to add a constructor. This will allow us to initialize the window class.

To do this, add inside the class:

Window(const char* title, int w, int h) {
  this->windowTitle = title;
  this->width = w;
  this->height = h;
}

This, very simply, allows us to initialize our class. However, it still doesn't display a window.

Step 6: Initializing the Window

Now, we're going to actually create a window. But first, we'll need to define a few functions in our header file. Add to the window class:

bool Init();
int Run();

These functions haven't been given a body yet - and there's a good reason for that. We're going to define these functions within a c++ file, in order to keep the header file clear.

Helpfully, Visual Studio creates both a header and c++ file whenever you create a class. Go into Window.cpp and add the following:

bool Window::Init() {
	if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
		printf("Error initializing SDL2: %s\n", SDL_GetError());
		return false;
	}
	printf("DEBUG: SDL Successfully Initialized!\n");
}


int Window::Run() {
	if (!this->Init()) return -1;
}

So far, we're just initializing SDL inside the Init function, which still doesn't make our window. So, lets change that!

bool Window::Init() {
	if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
		printf("Error initializing SDL2: %s\n", SDL_GetError());
		return false;
	}
	printf("DEBUG: SDL Successfully Initialized!\n");


	this->win = SDL_CreateWindow(
		this->windowTitle,
		SDL_WINDOWPOS_CENTERED,
		SDL_WINDOWPOS_CENTERED,
		this->width,
		this->height,
		0
	);


	if (!this->win) {
		printf("Error initializing Window: %s\n", SDL_GetError());
		return false;
	}
	printf("DEBUG: Window Successfully Initialized!\n");


	this->renderer = SDL_CreateRenderer(
		this->win,
		-1,
		SDL_RENDERER_ACCELERATED
	);


	if (!this->renderer) {
		printf("Error initializing Renderer: %s\n", SDL_GetError());
		return false;
	}
	printf("DEBUG: Renderer Successfully Initialized!\n");
}

This function now sets up a window for use. This instructable assumes you have an understanding of how SDL2 works, so I won't go into any detail on the functionality of this.

Step 7: Create a Window!

Now, go back to your main c++ file, and replace the code with

#include "Window.h"


int main()
{
    const char* title = "Hello World!";
    const int width = 640;
    const int height = 480;


    Window* win = new Window(title, width, height);


    return win->Run();
}

Now, upon running the code... you'll get an error.

That is because "main" is already defined by SDL. To fix this, after including SDL in Window.h, simply add:

#undef main

Now, if you run the code, a window will appear and then close.

Lets get it to stay open, shall we?

Step 8: Window Loop & Event Polling

Now, we need to make the window stay open. To do this, we first need to add the following variables to the window class:

bool running = true;
SDL_Event event;

Also, we need to define a new function:

void PollEvents();

Back in Window.cpp, we now need to add to Window::Run():

while (this->running) {
		this->PollEvents();
}

Now, when we run the code, we get an unresponsive window. To fix this, we need to poll events, which we will define the function for now.

void Window::PollEvents() {
	while (SDL_PollEvent(&this->event)) {
		switch (event.type) {
		case SDL_QUIT:
			this->running = false;
			break;
		}
	}
}

Now, when you run the code, you will see a blank window with the title "Hello World!"

Step 9: Rainbow Square

Now, lets create a basic display - A rainbow square!

First, lets change the screen size in the main file:

const int width = 256;
const int height = 256;

We will need the window to be square, and to be 256x256 pixels in size.

Now, add into the while loop in Window::Run():

for (int y = 0; y < this->height; y++) {
	for (int x = 0; x < this->width; x++) {
		SDL_SetRenderDrawColor(this->renderer, x, y, 153, 255);
		SDL_RenderDrawPoint(this->renderer, x, y);
	}
}


SDL_RenderPresent(this->renderer);

Upon running the program, you'll get a lovely coloured window!

Step 10: Make It Scaleable!

Currently, the window is very small. However, if the window is resized, the image tiles. To fix this, we'll simply floor the value of the width divided by 256.

Change the inner for loop to this:

SDL_SetRenderDrawColor(this->renderer, floor(x/(this->width/256)), floor(y/(this->height/256)), 153, 255);
SDL_RenderDrawPoint(this->renderer, x, y);

Now, the window will draw correctly, assuming the width and height are both multiples of 256.