Hello Cornflower

How to 3D

Chapter 1: Points and Lines

Hello Cornflower

Every graphics concept that we discuss in this course will be made concrete in code. You need a place to write this code. Let's make that place right now as you write your first renderer, which will display a cornflower blue rectangle.

Repository

Your instructor has created a single Git repository for all your project and experimental code. Each of your project milestones will be stored in this single repository. The repository is currently only on GitHub. You must clone it to your local computer by following these steps:

Visit the course GitHub organization using the link on Canvas.
Find your repository and copy its URL.
Open Visual Studio Code.
Select View / Command Palette.
Enter the command Git: Clone and paste in the URL. Then select a folder that will hold your cloned repository. Since the repository is empty, the clone will be empty.

You only need to clone your repository once. As the semester progresses, you will make changes to the folder you just made and push them up to GitHub.

Global Configuration

You will place many renderers in your repository throughout the semester. Let's perform some global configuration now that will impact all of these future renderers.

Ensure your empty top-level repository folder is open in Visual Studio Code. Each project that you create this semester will be a subfolder in this top-level folder.

Create a file named .gitignore and add these lines:

.DS_Store
Thumbs.db
node_modules
.DS_Store
Thumbs.db
node_modules

These are the names of files that should never be added to version control. The first two are files created by your operating system to cache thumbnail versions of any image files. These thumbnails are shown in your operating system's file explorer. The folder node_modules holds all the library code that your project depends on. Its content generally consumes a lot of disk space and can be downloaded afresh at any time.

By placing .gitignore at the top level of your repository, it will recursively apply to all future renderers.

Renderer

Now it's time to add your first renderer to your repository. There are two ways we could walk you through this process. We could start you off with a project that is immediately runnable and then lead you through a series of errors and their fixes as you develop a functioning renderer. Or we could take a more manicured path that postpones running the code—and discovering errors—until the very end. Both approaches have their merit. Let's go with the second because bouncing from error to error is exhausting.

Create a new subfolder named hello-cornflower. Open the terminal and enter this command so that your commands will be run in the context of your project folder instead of the course folder:

cd hello-cornflower
cd hello-cornflower

In your subfolder, create a file named index.html and add this HTML code:

<!DOCTYPE html>
<html>
  <head>
    <title>Hello, Cornflower</title>
    <link rel="icon" href="data:,">
    <link rel="stylesheet" href="/style.css">
  </head>
  <body>
    <canvas id="canvas"></canvas>
    <script type="module" src="./src/main.ts"></script>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <title>Hello, Cornflower</title>
    <link rel="icon" href="data:,">
    <link rel="stylesheet" href="/style.css">
  </head>
  <body>
    <canvas id="canvas"></canvas>
    <script type="module" src="./src/main.ts"></script>
  </body>
</html>

The canvas element is the rectangle that will get filled with pixels. The script element will load in your JavaScript code. The link tag loads in a stylesheet that alters the appearance of your page elements.

If you opened index.html as a file in your web browser right now, the developer console would complain that style.css and main.ts can't be found. Let's add these files.

Create a folder named public to store static assets like images and stylesheets. Create file public/style.css with this CSS styling:

body {
  margin: 0;
}

#canvas {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  height: 100vh;
}
body {
  margin: 0;
}

#canvas {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  height: 100vh;
}

This stylesheet removes the whitespace margin around the page's body element and makes the canvas element fill the browser window. The index.html file you created earlier loads in this stylesheet using a link tag.

Create a folder named src to hold source files. Create file src/main.ts and add this TypeScript:

let canvas: HTMLCanvasElement;

async function initialize() {
  canvas = document.getElementById('canvas') as HTMLCanvasElement;
  window.gl = canvas.getContext('webgl2') as WebGL2RenderingContext;

  // Initialize other graphics state as needed.

  // Event listeners
  window.addEventListener('resize', () => resizeCanvas());

  resizeCanvas();  
}

function render() {
  gl.viewport(0, 0, canvas.width, canvas.height);
  gl.clearColor(0.392, 0.584, 0.929, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function resizeCanvas() {
  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;
  render();
}

window.addEventListener('load', () => initialize());
let canvas: HTMLCanvasElement;

async function initialize() {
  canvas = document.getElementById('canvas') as HTMLCanvasElement;
  window.gl = canvas.getContext('webgl2') as WebGL2RenderingContext;

  // Initialize other graphics state as needed.

  // Event listeners
  window.addEventListener('resize', () => resizeCanvas());

  resizeCanvas();  
}

function render() {
  gl.viewport(0, 0, canvas.width, canvas.height);
  gl.clearColor(0.392, 0.584, 0.929, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function resizeCanvas() {
  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;
  render();
}

window.addEventListener('load', () => initialize());

The initialize function grabs a reference to the page's canvas element and gives it a WebGL context, an environment for executing WebGL commands. It runs exactly once, as the application starts up. The function render colors in the pixels of the framebuffer and runs throughout the lifetime of the renderer. Currently render only clears the canvas to the color cornflower. The first three numbers passed to clearColor are the red, green, and blue intensities that mix to make cornflower. The fourth number is the color's opacity. A value of 1 means that the color is completely opaque, that you can't see anything behind it. The resizeCanvas function is called whenever the window changes size. It resizes the canvas so that it matches the window and re-renders the scene.

We're using TypeScript in this course to discover errors early. The global variable gl in main.ts doesn't have a type declared, which will be a problem later on. We could declare it at the top of this file, but you are eventually going to refer to it in other files too. Let's instead declare it in a special file for global declarations. Create file src/globals.d.ts with this content:

interface Window {
  gl: WebGL2RenderingContext;
}

declare const gl: WebGL2RenderingContext;
interface Window {
  gl: WebGL2RenderingContext;
}

declare const gl: WebGL2RenderingContext;

The main content files are all now in place. But loading index.html as a file in your browser will still fail. That's because this renderer isn't a standalone web page. The TypeScript needs to get translated into JavaScript, and all the code and assets need to be bundled together into a web app.

App

Before you can turn your renderer into a web app, it needs to be a Node.js project. Create file hello-cornflower/package.json and add this configuration:

{
  "name": "hello-cornflower",
  "type": "module",
  "scripts": {
    "start": "vite --open",
    "build": "tsc && vite build",
    "driver": "tsc && node build/driver.js" 
  }
}
{
  "name": "hello-cornflower",
  "type": "module",
  "scripts": {
    "start": "vite --open",
    "build": "tsc && vite build",
    "driver": "tsc && node build/driver.js" 
  }
}

All Node.js projects have this file; it describes the project, its dependencies, and any custom commands that you may want to run. This configuration provides two commands: start and build. The start command is the most important one for this course. It starts up a local web server and opens your index.html file in your browser.

The utility vite (pronounced VEET) is a bundler that packs your code, HTML, stylesheets, and images into a standalone web page. The utility tsc is the TypeScript compiler that translates TypeScript into JavaScript. Install your project's Vite and TypeScript dependencies by executing this command in your terminal:

npm install vite vite-plugin-checker typescript
npm install vite vite-plugin-checker typescript

Inspect package.json. See the dependencies that have been added? Observe the node_modules folder that has been created in the hello-cornflower directory. It will be kept out of version control by the .gitignore file you created earlier. The file package-lock.json has also been created. It tracks the version numbers of the dependencies you are currently using.

The TypeScript compiler needs to be configured. Create hello-cornflower/tsconfig.json with this JSON configuration:

{
  "compilerOptions": {
    "target": "es2020",
    "module": "nodenext",
    "skipLibCheck": true,
    "moduleResolution": "nodenext",
    "outDir": "./build",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noImplicitAny": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"]
}
{
  "compilerOptions": {
    "target": "es2020",
    "module": "nodenext",
    "skipLibCheck": true,
    "moduleResolution": "nodenext",
    "outDir": "./build",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noImplicitAny": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"]
}

By design, Vite only shows type errors when you issue a build, but not as you iteratively develop your renderer—which is exactly when you want to see these errors. The vite-plugin-checker makes these errors visible as you write code. Enable this checker by creating file hello-cornflower/vite.config.js with this content:

import checker from 'vite-plugin-checker';

export default {
  plugins: [
    checker({
      typescript: true,
    }),
  ],
};
import checker from 'vite-plugin-checker';

export default {
  plugins: [
    checker({
      typescript: true,
    }),
  ],
};

You've done it; there's no more setup. This hello-world of computer graphics and web apps isn't so straighforward, is it? Run a local web server that serves out your app with this command:

npm run start
npm run start

View the app in your browser, which should open automatically. Do you see a big cornflower rectangle filling the browser viewport? If not, check that your files match those listed above. Seek assistance as needed.

Try changing the clear color in main.ts. As soon as you save the file, Vite will automatically reload the page in the browser.

Synchronizing to GitHub

Your files are only in your local clone of your repository. You need to commit your changes and then push them up to GitHub. You should do this after every work session, even if your code is broken or your task isn't done. By commiting and pushing regularly, your work is backed up and your instructor can see your code if you have a question.

Commit and push your code by following these steps.

Open the source control management panel in Visual Studio Code by selecting View / SCM. The panel lists all the files that are new to the repository or that have changes since your last commit.
Hover your mouse over each file and click the + button to add the changed file to your commit. Or, to add all changes, hover over the the Changes heading and click the + button.
Add a short statement that describes the changes in the message box. For example, you might write Add cornflower renderer. or Fix missing triangles. or Refactor particle system.
Click the check button. The changes are committed only to your local repository.
Click Sync Changes. Or click the … icon and select Pull, Push / Push.

Visit your repository on github.com and make sure you see your changes. If you can't see them, then your instructor can't either.

For future projects, either repeat the steps above, starting at the Renderer section, or copy the hello-cornflower directory.

← Software SetupVerbs and Nouns →