Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6b4a7e3
colored cube
18smlee Sep 14, 2021
2bc30ae
first draft
18smlee Sep 15, 2021
f813bf2
added custom shader
18smlee Sep 15, 2021
410db81
Update README.md
18smlee Sep 15, 2021
6e37dfe
fixed ambient term
18smlee Sep 15, 2021
3f4af44
Merge branch 'master' of https://github.com/18smlee/hw00-webgl-intro
18smlee Sep 15, 2021
89c28d9
revert to original icosphere and add planet shaders
18smlee Sep 21, 2021
8ce0902
smushing vertices in y direction
18smlee Sep 21, 2021
a27e857
quartere crater biome
18smlee Sep 23, 2021
a311e93
cool swirly ball
18smlee Sep 23, 2021
d10ef68
black and white planet
18smlee Sep 25, 2021
e89784b
rocks and beach added
18smlee Sep 27, 2021
7a149fe
biomes done
18smlee Sep 28, 2021
a7bb639
gui added with all biomes
18smlee Sep 28, 2021
ab20686
tried to implement normals - not working
18smlee Sep 29, 2021
51394a8
tried to implement normals - not working
18smlee Sep 29, 2021
0ff4b5a
forest density added
18smlee Sep 29, 2021
a29d89a
update readme and enable shading
18smlee Sep 29, 2021
7f33201
add images
18smlee Sep 29, 2021
776a5dc
image into readme
18smlee Sep 29, 2021
8111753
Update README.md
18smlee Sep 29, 2021
d9302d2
update readme
18smlee Sep 29, 2021
0d7fc33
Merge branch 'master' of https://github.com/18smlee/hw00-webgl-intro
18smlee Sep 29, 2021
eab440b
Update README.md
18smlee Sep 29, 2021
2f5aad9
added normals and blinn phong shading
18smlee Sep 29, 2021
cce587b
Merge branch 'master' of https://github.com/18smlee/hw00-webgl-intro
18smlee Sep 29, 2021
ca17214
fix photo
18smlee Sep 29, 2021
3e77113
update readme
18smlee Sep 29, 2021
baba05a
water lines up
18smlee Sep 30, 2021
f020844
got rid of color and tesselation gui
18smlee Sep 30, 2021
2507aa6
Update README.md
18smlee Oct 6, 2021
8c589bc
Update README.md
18smlee Oct 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 17 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,28 @@
# HW 0: Noisy Planet Part 1 (Intro to Javascript and WebGL)
# CIS 566 Project 1: Noisy Planets
Name : Samantha Lee
PennKey : smlee18

<p align="center">
<img width="360" height="360" src="https://user-images.githubusercontent.com/1758825/132532354-e3a45402-e484-499e-bfa7-2d73b9f2c946.png">
</p>
<p align="center">(source: Ken Perlin)</p>
Sources: https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83

## Objective
- Check that the tools and build configuration we will be using for the class works.
- Start learning Typescript and WebGL2
- Practice implementing noise
Live Demo: https://18smlee.github.io/hw00-webgl-intro/

## Forking the Code
Rather than cloning the homework repository, please __fork__ the code into your own repository using the `Fork` button in the upper-right hand corner of the Github UI. This will enable you to have your own personal repository copy of the code, and let you make a live demo (described later in this document).
![image](images/planet.PNG)

## Running the Code
## Biomes
There are four different types of terrain: ocean, beach, forest, and mountains. The elevation of a given vertex is based on a fractional brownian noise function from the above source. The lighter noise values correspond to higher elevation and the darker noise values correspond to lower elevation.

1. [Install Node.js](https://nodejs.org/en/download/). Node.js is a JavaScript runtime. It basically allows you to run JavaScript when not in a browser. For our purposes, this is not necessary. The important part is that with it comes `npm`, the Node Package Manager. This allows us to easily declare and install external dependencies such as [dat.GUI](https://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage), and [glMatrix](http://glmatrix.net/).
I then applied gain and bias functions to certain ranges that transition between different biome thresholds. This was to give the slope of the mountains and beaches a more curved look, rather than just being extruded straight up from the surface of the sphere.

2. Using a command terminal, run `npm install` in the root directory of your project. This will download all of those dependencies.
The water has a warped noise function applied to it to give it an unconvential marbled look. The vertices are also slightly raised with the moving water to emulate slow moving waves.

3. Do either of the following (but we highly recommend the first one for reasons we will explain later).
The land biome has a couple of different noise functions applied to it. There is one to soften the look of the land by interpolating a darker green into a lighter green. There is also a forestNoise value that affects both the color and the vertices. This noise function is made to simulate vegetation or forests scattered across the planet. I created the forest noise function with an fbm function with a lower frequency and a perlin noise function with a higher frequency and interpolated between the two. This gave an interesting output with more variation between sizes of random "blobs", more closely mimicking natural vegetaion. The user can control the density of the forest with the forest density parameter.

a. Run `npm start` and then go to `localhost:5660` in your web browser
The mountain biome has its own mountainNoise fbm function to further its surface and color to mimick natural rock formations.

b. Run `npm run build` and then go open `dist/index.html` in your web browser
## Color Palette and Terrain Frequency
I created two color palettes: earth and alien. The earth color palette creates a planet most resembling earth, with green forests, blue water, and grey mountains. The alien color palette however has a more foreign look with orange beaches, purple vegetation, and violet mountains. The final color palette is an interpolation between these two palettes, with the interpolation value being controlled by the user. So the user can decide how earthlike or alienlike they want their planet to be!

## Module Bundling
One of the most important dependencies of our projects is [Webpack](https://webpack.js.org/concepts/). Webpack is a module bundler which allows us to write code in separate files and use `import`s and `export`s to load classes and functions for other files. It also allows us to preprocess code before compiling to a single file. We will be using [Typescript](https://www.typescriptlang.org/docs/home.html) for this course which is Javascript augmented with type annotations. Webpack will convert Typescript files to Javascript files on compilation and in doing so will also check for proper type-safety and usage. Read more about Javascript modules in the resources section below.
The terrain frequency can also be controled by the user. This is a frequency value that is multiplied by the noise input of the original noise function that determines the elevation of a point. Giving control of the terrain's variation to the user allows them to create differently shaped planets in the blink of an eye!

## Developing Your Code
All of the JavaScript code is living inside the `src` directory. The main file that gets executed when you load the page as you may have guessed is `main.ts`. Here, you can make any changes you want, import functions from other files, etc. The reason that we highly suggest you build your project with `npm start` is that doing so will start a process that watches for any changes you make to your code. If it detects anything, it'll automagically rebuild your project and then refresh your browser window for you. Wow. That's cool. If you do it the other way, you'll need to run `npm build` and then refresh your page every time you want to test something.

We would suggest editing your project with Visual Studio Code https://code.visualstudio.com/. Microsoft develops it and Microsoft also develops Typescript so all of the features work nicely together. Sublime Text and installing the Typescript plugins should probably work as well.

## Assignment Details
1. Take some time to go through the existing codebase so you can get an understanding of syntax and how the code is architected. Much of the code is designed to mirror the class structures used in CIS 460's OpenGL assignments, so it should hopefully be somewhat familiar.
2. Take a look at the resources linked in the section below. Definitely read about Javascript modules and Typescript. The other links provide documentation for classes used in the code.
3. Add a `Cube` class that inherits from `Drawable` and at the very least implement a constructor and its `create` function. Then, add a `Cube` instance to the scene to be rendered.
4. Read the documentation for dat.GUI below. Update the existing GUI in `main.ts` with a parameter to alter the color passed to `u_Color` in the Lambert shader.
5. Write a custom fragment shader that implements FBM, Worley Noise, or Perlin Noise based on 3D inputs (as opposed to the 2D inputs in the slides). This noise must be used to modify your fragment color. If your custom shader is particularly interesting, you'll earn some bonus points.
6. Write a custom vertex shader that uses a trigonometric function (e.g. `sin`, `tan`) to non-uniformly modify your cube's vertex positions over time. This will necessitate instantiating an incrementing variable in your Typescript code that you pass to your shader every tick. Refer to the base code's methods of passing variables to shaders if you are unsure how to do so.
7. Feel free to update any of the files when writing your code. The implementation of the `OpenGLRenderer` is currently very simple.

## Making a Live Demo
When you push changes to the `master` branch of your repository on Github, a Github workflow will run automatically which builds your code and pushes the build to a new branch `gh-pages`. The configuration file which handles this is located at `.github/workflows/build-and-deploy.yml`. If you want to modify this, you can read more about workflows [here](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions).

Once your built code is pushed to `gh-pages`, Github can automatically publish a live site. Configure that by:

1. Open the Settings tab of your repository in Github.

2. Scroll down to the Pages tab of the Settings (in the table on the left) and choose which branch to make the source for the deployed project. This should be the `gh-pages` branch which is automatically created after the first successful build of the `master` branch.

3. Done! Now, any new commits on the `master` branch will be built and pushed to `gh-pages`. The project should be visible at http://username.github.io/repo-name.


To check if everything is on the right track:

1. Make sure the `gh-pages` branch of your repo has a files called `index.html`, `bundle.js`, and `bundle.js.map`

2. In the settings tab of the repo, under Pages, make sure it says your site is published at some url.

## Submission
1. Create a pull request to this repository with your completed code.
2. Update README.md to contain a solid description of your project with a screenshot of some visuals, and a link to your live demo.
3. Submit the link to your pull request on Canvas, and add a comment to your submission with a hyperlink to your live demo.
4. Include a link to your live site.

## Resources
- Javascript modules https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
- Typescript https://www.typescriptlang.org/docs/home.html
- dat.gui https://workshop.chromeexperiments.com/examples/gui/
- glMatrix http://glmatrix.net/docs/
- WebGL
- Interfaces https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API
- Types https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Types
- Constants https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants
## Shading
I've applied lambert shading to the entire planet with a slowly rotating light. There is blinn phong shading on just the mountains to accentuate their jagged formations.
Binary file added images/Capture.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/alien.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/beach.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/half_alien.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/high_freq.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/low_freq.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/mountains.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/planet.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/water.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/Camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class Camera {
vec3.add(this.target, this.position, this.direction);
mat4.lookAt(this.viewMatrix, this.controls.eye, this.controls.center, this.controls.up);
}

getEye() {
return this.controls.eye;
}
};

export default Camera;
107 changes: 107 additions & 0 deletions src/geometry/Cube.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import {vec3, vec4} from 'gl-matrix';
import Drawable from '../rendering/gl/Drawable';
import {gl} from '../globals';

class Cube extends Drawable {
indices: Uint32Array;
positions: Float32Array;
normals: Float32Array;
center: vec4;

constructor(center: vec3) {
super(); // Call the constructor of the super class. This is required.
this.center = vec4.fromValues(center[0], center[1], center[2], 1);
}

create() {

this.indices = new Uint32Array([0, 1, 2,
0, 2, 3,
4, 5, 6,
4, 6, 7,
8, 9, 10,
8, 10, 11,
12, 13, 14,
12, 14, 15,
16, 17, 18,
16, 18, 19,
20, 21, 22,
20, 22, 23]);
this.normals = new Float32Array([0, 0, -1, 0,
0, 0, -1, 0,
0, 0, -1, 0,
0, 0, -1, 0,

0, 0, 1, 0,
0, 0, 1, 0,
0, 0, 1, 0,
0, 0, 1, 0,

-1, 0, 0, 0,
-1, 0, 0, 0,
-1, 0, 0, 0,
-1, 0, 0, 0,

1, 0, 0, 0,
1, 0, 0, 0,
1, 0, 0, 0,
1, 0, 0, 0,

0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,
0, 1, 0, 0,

0, -1, 0, 0,
0, -1, 0, 0,
0, -1, 0, 0,
0, -1, 0, 0,]);
this.positions = new Float32Array([-1, -1, 1, 1,
-1, -1, -1, 1,
1, -1, -1, 1,
1, -1, 1, 1,

-1, 1, 1, 1,
-1, 1, -1, 1,
1, 1, -1, 1,
1, 1, 1, 1,

-1, 1, 1, 1,
-1, 1, -1, 1,
-1, -1, -1, 1,
-1, -1, 1, 1,

1, 1, 1, 1,
1, 1, -1, 1,
1, -1, -1, 1,
1, -1, 1, 1,

-1, 1, 1, 1,
-1, -1, 1, 1,
1, -1, 1, 1,
1, 1, 1, 1,

-1, 1, -1, 1,
-1, -1, -1, 1,
1, -1, -1, 1,
1, 1, -1, 1,]);

this.generateIdx();
this.generatePos();
this.generateNor();

this.count = this.indices.length;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.bufIdx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);

gl.bindBuffer(gl.ARRAY_BUFFER, this.bufNor);
gl.bufferData(gl.ARRAY_BUFFER, this.normals, gl.STATIC_DRAW);

gl.bindBuffer(gl.ARRAY_BUFFER, this.bufPos);
gl.bufferData(gl.ARRAY_BUFFER, this.positions, gl.STATIC_DRAW);

console.log(`Created cube`);
}
};

export default Cube;
61 changes: 49 additions & 12 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {vec3} from 'gl-matrix';
import {vec3, vec4} from 'gl-matrix';
const Stats = require('stats-js');
import * as DAT from 'dat.gui';
import Icosphere from './geometry/Icosphere';
import Cube from './geometry/Cube';
import Square from './geometry/Square';
import OpenGLRenderer from './rendering/gl/OpenGLRenderer';
import Camera from './Camera';
Expand All @@ -11,19 +12,30 @@ import ShaderProgram, {Shader} from './rendering/gl/ShaderProgram';
// Define an object with application parameters and button callbacks
// This will be referred to by dat.GUI's functions that add GUI elements.
const controls = {
tesselations: 5,
'terrain frequency': 0.5,
'earth to alien': 0.0,
'forest density': 0.2,
'Load Scene': loadScene, // A function pointer, essentially
};

let icosphere: Icosphere;
let cube: Cube;
let square: Square;

let prevTesselations: number = 5;
let cubeColor: vec4 = vec4.fromValues(1, 0, 1, 1);

// Procedural Controls
let terrainFreq: number = 0.5;
let earthToAlien: number = 0.0;
let forestScale: number = 0.2;

let time: number = 0;
let tesselations: number = 5;

function loadScene() {
icosphere = new Icosphere(vec3.fromValues(0, 0, 0), 1, controls.tesselations);
icosphere = new Icosphere(vec3.fromValues(0, 0, 0), 1, tesselations);
icosphere.create();
square = new Square(vec3.fromValues(0, 0, 0));
square.create();
}

function main() {
Expand All @@ -37,7 +49,10 @@ function main() {

// Add controls to the gui
const gui = new DAT.GUI();
gui.add(controls, 'tesselations', 0, 8).step(1);

gui.add(controls, 'terrain frequency', 0.3, 2.0).step(0.05).onChange(function() { terrainFreq = controls['terrain frequency'] });
gui.add(controls, 'earth to alien', 0.0, 1.0).step(0.05).onChange(function() { earthToAlien = controls['earth to alien'] });
gui.add(controls, 'forest density', 0.0, 1.0).step(0.05).onChange(function() { forestScale = controls['forest density'] });
gui.add(controls, 'Load Scene');

// get canvas and webgl context
Expand All @@ -56,30 +71,52 @@ function main() {
const camera = new Camera(vec3.fromValues(0, 0, 5), vec3.fromValues(0, 0, 0));

const renderer = new OpenGLRenderer(canvas);
renderer.setClearColor(0.2, 0.2, 0.2, 1);

// Create starry outerspace clear color
let darkBlue = vec4.fromValues(0.0 / 11.0, 0.0 / 255.0, .0 / 255.0, 1);
renderer.setClearColor(darkBlue);
gl.enable(gl.DEPTH_TEST);

const lambert = new ShaderProgram([
new Shader(gl.VERTEX_SHADER, require('./shaders/lambert-vert.glsl')),
new Shader(gl.FRAGMENT_SHADER, require('./shaders/lambert-frag.glsl')),
]);

const planet_shader = new ShaderProgram([
new Shader(gl.VERTEX_SHADER, require('./shaders/planet-vert.glsl')),
new Shader(gl.FRAGMENT_SHADER, require('./shaders/planet-frag.glsl')),
]);

// const custom_shader = new ShaderProgram([
// new Shader(gl.VERTEX_SHADER, require('./shaders/custom-vert.glsl')),
// new Shader(gl.FRAGMENT_SHADER, require('./shaders/custom-frag.glsl')),
// ]);

// This function will be called every frame
function tick() {

planet_shader.setTime(time);
planet_shader.setTerrainFreq(terrainFreq);
planet_shader.setEarthToAlien(earthToAlien);
planet_shader.setForestScale(forestScale);
planet_shader.setCamera([camera.getEye()[0], camera.getEye()[1], camera.getEye()[2], 1.0]);

time++;

camera.update();
stats.begin();
gl.viewport(0, 0, window.innerWidth, window.innerHeight);
renderer.clear();
if(controls.tesselations != prevTesselations)
if(tesselations != prevTesselations)
{
prevTesselations = controls.tesselations;
prevTesselations = 5;
icosphere = new Icosphere(vec3.fromValues(0, 0, 0), 1, prevTesselations);
icosphere.create();
}
renderer.render(camera, lambert, [

renderer.render(camera, planet_shader, [
icosphere,
// square,
]);
], cubeColor);
stats.end();

// Tell the browser to call `tick` again whenever it renders a new frame
Expand Down
9 changes: 5 additions & 4 deletions src/rendering/gl/OpenGLRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ class OpenGLRenderer {
constructor(public canvas: HTMLCanvasElement) {
}

setClearColor(r: number, g: number, b: number, a: number) {
gl.clearColor(r, g, b, a);
setClearColor(color: vec4) {
let black = vec4.fromValues(0.0 / 255.0, 0.0 / 255.0, 0.0 / 255.0, 1.0);
gl.clearColor(color[0], color[1], color[2], color[3]);
}

setSize(width: number, height: number) {
Expand All @@ -22,10 +23,10 @@ class OpenGLRenderer {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}

render(camera: Camera, prog: ShaderProgram, drawables: Array<Drawable>) {
render(camera: Camera, prog: ShaderProgram, drawables: Array<Drawable>, cubeColor: vec4) {
let model = mat4.create();
let viewProj = mat4.create();
let color = vec4.fromValues(1, 0, 0, 1);
let color = cubeColor;

mat4.identity(model);
mat4.multiply(viewProj, camera.projectionMatrix, camera.viewMatrix);
Expand Down
Loading