Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Empty file modified .gitignore
100644 → 100755
Empty file.
133 changes: 22 additions & 111 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,123 +1,34 @@
# Shaders

# Project 5: Shaders
### Lambert
Standard lambert shading by default!

## Project Instructions
### Toon
In this Toon shader, we band color intensities together and set them to a floored value. We also take the vertex normal and color the fragment by the view direction if it exceeds a certain angle. This allows for a fun rainbowy outline of a character who seems to be shaded by a few colors only.

Implement at least 75 points worth of shaders from the following list. We reserve the right to grant only partial credit for shaders that do not meet our standards, as well as extra credit for shaders that we find to be particularly impressive.
### Lit Sphere
In this shader, we load MatCap spheres that were pre-generated. We then map the normal-space to UV-space using one easy trick that pipeline engineers don't want you to know! We extract the color from that texture using the corresponding UVs (mapped from normals) and set the vertex color. The end result is really cool materials that are applied to our model. This could have also been done as a fragment shader but I think it's a bit easier as a vertex shader and works well enough for our mesh which has high vertex density.

Some of these shading effects were covered in lecture -- some were not. If you wish to implement the more complex effects, you will have to perform some extra research. Of course, we encourage such academic curiosity which is why we’ve included these advanced shaders in the first place!
# Post Processes

Document each shader you implement in your README with at least a sentence or two of explanation. Well-commented code will earn you many brownie (and probably sanity) points.
### Grayscale
This is our Hello World, and is just a standard RGB -> Luminosity map.

If you use shadertoy or any materials as reference, please properly credit your sources in the README and on top of the shader file. Failing to do so will result in plagiarism and will significantly reduce your points.
### Tint
We add a flat color to our fragment shaded values.

Examples: [https://cis700-procedural-graphics.github.io/Project5-Shaders/](https://cis700-procedural-graphics.github.io/Project5-Shaders/)
### Sobel
We take the two gradient Sobel kernels and apply a high pass to the image. We threshold the intensity values of both horizontal and vertical gradients and set the fragment colors to black beyond a certain point to highlight edges.

### 15 points each: Instagram-like filters
### Bloom
The pipeline is as follows: High pass, Blur, Additive Re-blending. The first step is a multi-target pass that generates a texture of only high intensity fragments and a texture of regular scene rendering. We then blur the high pass scene with a gaussian blur. The end result is additively recombined with the original. This yields a bloom effect.

- Tone mapping:
- Linear (+5 points)
- Reinhard (+5 points)
- Filmic (+5 points)
- Gaussian blur (no double counting with Bloom)
- Iridescence
- Pointilism
- Vignette
- Fish-eye bulge

### 25 points each:
- Bloom
- Noise Warp
- Hatching
- Lit Sphere ([paper](http://www.ppsloan.org/publications/LitSphere.pdf))
### Oil/Watercolor paintings
For each pixel, we band intensities but this time we keep track of the sum of the R, G, and B values of that intensity as well as how often an intensity appears (the mode). We take the most frequent intensity band and then do a reverse lookup of the total R, G, and B values of that intensity band. We then divide by the mode of the intensity to obtain a flat color that we apply to pixels. Because the mode intensity doesn't change that often over pixels, we get the blotchiness that is characteristic of oil/watercolor paintings.

### 37.5 points each:
- K-means color compression (unless you are extremely clever, the k-means clusterer has to be CPU side)
- Dithering
- Edge detection with Sobel filtering
- Uncharted 2 customizable filmic curve, following John Hable’s presetantion.
- Without Linear, Reinhard, filmic (+10 points)
- With all of linear, Reinhard, filmic (+10 points)
- Customizable via GUI (+17.5 points)
- Controlling Exposure (4 points)
- Side by side comparison between linear, Reinhard, filmic, and Uncharted2 (13.5 points).
### Dithering
Since the traditional serial Dithering algorithms aren't parallelizable in glsl, we resort to using other approximation methods. In particular, we perform Ordered Dithering using a generated Bayer matrix to "push" error around. We add the error to the pixel and round to either 1 or 0 (full color or none at all). The end result is pretty neat.

### 5 points - Interactivity
Implement a dropdown GUI to select different shader effects from your list.

### ??? points
Propose your own shading effects!

### For the overachievers:
Weave all your shading effects into one aesthetically-coherent scene, perhaps by incorporating some of your previous assignments!


## Getting Started

### main.js

`main.js` is responsible for setting up the scene with the Mario mesh, initializing GUI and camera, etc.

### Adding Shaders

To add a shader, you'll want to add a file to the `src/shaders` or `src/post` folder. As examples, we've provided two shaders `lambert.js` and `grayscale.js`. Here, I will give a brief overview of how these work and how everything hooks together.

**shaders/lambert.js**

IMPORTANT: I make my lambert shader available by exporting it in `shaders/index.js`.

```javascript
export {default as Lambert} from './Lambert'
```

Each shader should export a function that takes in the `renderer`, `scene`, and `camera`. That function should return a `Shader` Object.

`Shader.initGUI` is a function that will be called to initialize the GUI for that shader. in `lambert.js`, you can see that it's here that I set up all the parameters that will affect my shader.

`Shader.material` should be a `THREE.ShaderMaterial`. This should be pretty similar to what you've seen in previous projects. `Shader.material.vertexShader` and `Shader.material.fragmentShader` are the vertex and fragment shaders used.

At the bottom, I have the following snippet of code. All it does is bind the Mario texture once it's loaded.

```javascript
textureLoaded.then(function(texture) {
Shader.material.uniforms.texture.value = texture;
});
```

So when you change the Shader parameter in the GUI, `Shader.initGUI(gui)` will be called to initialize the GUI, and then the Mario mesh will have `Shader.material` applied to it.

**post/grayscale.js**

GUI parameters here are initialized the same way they are for the other shaders.

Post process shaders should use the THREE.js `EffectComposer`. To set up the grayscale filter, I first create a new composer: `var composer = new EffectComposer(renderer);`. Then I add a a render pass as the first pass: `composer.addPass(new EffectComposer.RenderPass(scene, camera));`. This will set up the composer to render the scene as normal into a buffer. I add my filter to operate on that buffer: `composer.addPass(GrayscaleShader);`, and mark it as the final pass that will write to the screen `GrayscaleShader.renderToScreen = true;`

GrayscaleShader is a `EffectComposer.ShaderPass` which basically takes the same arguments as `THREE.ShaderMaterial`. Note, that one uniform that will have to include is `tDiffuse`. This is the texture sampler which the EffectComposer will automatically bind the previously rendered pass to. If you look at `glsl/grayscale-frag.glsl`, this is the texture we read from to get the previous pixel color: `vec4 col = texture2D(tDiffuse, f_uv);`.

IMPORTANT: You initially define your shader passes like so:

```javascript
var GrayscaleShader = new EffectComposer.ShaderPass({
uniforms: {
tDiffuse: {
type: 't',
value: null
},
u_amount: {
type: 'f',
value: options.amount
}
},
vertexShader: require('../glsl/pass-vert.glsl'),
fragmentShader: require('../glsl/grayscale-frag.glsl')
});
```

BUT, if you want to modify the uniforms, you need to do so like so: `GrayscaleShader.material.uniforms.u_amount.value = val;`. Note the extra `.material` property.

## Deploy

1. Create a `gh-pages` branch on GitHub
2. Do `npm run build`
3. Commit and add all your changes.
4. Do `npm run deploy`
### Pointilism
Really simple algorithm that goes as follows: take the intensity/luminosity of pixel, generate a random float, if the intensity/luminosity exceeds that random value, we make the pixel white, otherwise black.
Binary file added img/Thumbs.db
Binary file not shown.
Binary file added img/mario1.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 img/mario2.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 img/mario3.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 img/mario4.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 img/mario5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified index.html
100644 → 100755
Empty file.
Binary file added mat/1.jpg
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 mat/2.jpg
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 mat/3.jpg
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 mat/4.jpg
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 mat/5.jpg
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 mat/Thumbs.db
Binary file not shown.
Empty file modified package.json
100644 → 100755
Empty file.
Binary file added src/assets/0.bmp
Binary file not shown.
Binary file added src/assets/1.bmp
Binary file not shown.
Binary file added src/assets/2.bmp
Binary file not shown.
Binary file added src/assets/3.bmp
Binary file not shown.
Binary file added src/assets/4.bmp
Binary file not shown.
Binary file added src/assets/5.bmp
Binary file not shown.
Binary file added src/assets/6.bmp
Binary file not shown.
Binary file added src/assets/Thumbs.db
Binary file not shown.
Empty file modified src/assets/wahoo.bmp
100644 → 100755
Empty file.
Empty file modified src/assets/wahoo.obj
100644 → 100755
Empty file.
15 changes: 15 additions & 0 deletions src/glsl/bloom-frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

uniform sampler2D tDiffuse;
uniform sampler2D tBuffer;

varying vec2 f_uv;

float luminosity(vec4 color) {
return (0.2126*color.x + 0.7152*color.y + 0.0722*color.z);
}

void main() {
vec4 regPass = texture2D(tDiffuse, f_uv);
vec4 highPass = texture2D(tBuffer, f_uv);
gl_FragColor = highPass + regPass;
}
54 changes: 54 additions & 0 deletions src/glsl/blur-frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
uniform sampler2D tDiffuse;
uniform vec2 u_scale;
uniform float u_std;
varying vec2 f_uv;

#define PI 3.1415926535897932384626433
#define E 2.7182818284590452353602874
#define INV2PI 0.159154943
#define SIZE 8

float luminosity(vec4 color) {
return (0.2126*color.x + 0.7152*color.y + 0.0722*color.z);
}

float gaussian(float x, float y, float std) {
float invStdSqr = 1.0 / pow(std, 2.0);
float exponent = -(pow(x, 2.0) + pow(y, 2.0)) * invStdSqr / 2.0;
return INV2PI * invStdSqr * exp(exponent);
}

void generateKernel(inout float kernel[SIZE * SIZE]) {
float total = 0.0;
float g = 0.0;
// Precompute gaussian kernel
for (int i = -SIZE / 2; i <= SIZE / 2; i++) {
for (int j = -SIZE / 2; j <= SIZE / 2; j++) {
g = gaussian(float(i), float(j), u_std);
kernel[(i + SIZE / 2) * SIZE + j] = g;
total += g;
}
}
// Normalize
for (int i = 0; i < SIZE * SIZE; i++) {
kernel[i] /= total;
}
}

void applyKernel(inout vec4 color, float kernel[SIZE * SIZE]) {
for (int i = -SIZE / 2; i <= SIZE / 2; i++) {
for (int j = -SIZE / 2; j <= SIZE / 2; j++) {
color += texture2D(tDiffuse, f_uv + u_scale * vec2(i,j)) * kernel[(i + SIZE / 2) * SIZE + j];
}
}
}

void main() {
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
float kernel[SIZE * SIZE];

generateKernel(kernel);
applyKernel(color, kernel);

gl_FragColor = color;
}
45 changes: 45 additions & 0 deletions src/glsl/dither-frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

uniform sampler2D tDiffuse;
uniform float u_threshold;
uniform float u_kernel[9];
uniform vec2 u_scale;
varying vec2 f_uv;

#define MAX_LEVEL 16
/*
Fast arbitrary bayer matrix generation algorithm borrowed from:
https://www.shadertoy.com/view/XtV3RG
*/
float GetBayerFromCoordLevel(vec2 pixelpos)
{
float finalBayer = 0.0;
float finalDivisor = 0.0;
float layerMult = 1.0;

for(float bayerLevel = float(MAX_LEVEL); bayerLevel >= 1.0; bayerLevel--) {
vec2 bayercoord = mod(floor(pixelpos.xy / exp2(bayerLevel) * 2.0),2.0);
layerMult *= 4.0;
float line0202 = bayercoord.x * 2.0;
finalBayer += mix(line0202,3.0 - line0202,bayercoord.y) / 3.0 * layerMult;
finalDivisor += layerMult;
}

return finalBayer / finalDivisor;
}

vec4 closest(vec4 color) {
float r, g, b;
r = floor(color.r + u_threshold);
g = floor(color.g + u_threshold);
b = floor(color.b + u_threshold);
vec4 rounded = vec4(r, g, b, 1.0);
return rounded;
}

void main() {
vec4 color = texture2D(tDiffuse, f_uv);
vec2 xy = f_uv / u_scale;
float bayer = GetBayerFromCoordLevel(xy) / 2.0;
color += vec4(bayer, bayer, bayer, 1.0);
gl_FragColor = closest(color);
}
Empty file modified src/glsl/grayscale-frag.glsl
100644 → 100755
Empty file.
28 changes: 28 additions & 0 deletions src/glsl/high-frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

uniform sampler2D tDiffuse;
uniform float u_threshold;
uniform vec2 u_scale;
uniform float u_std;
varying vec2 f_uv;

#define PI 3.1415926535897932384626433
#define E 2.7182818284590452353602874
#define INV2PI 0.159154943
#define SIZE 15

float intensity(vec4 color) {
return (color.x + color.y + color.z) / 3.0;
}

float luminosity(vec4 color) {
return (0.2126*color.x + 0.7152*color.y + 0.0722*color.z);
}

void main() {
vec4 texColor = texture2D(tDiffuse, f_uv);
if (luminosity(texColor) > u_threshold) {
gl_FragColor = texColor;
return;
}
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
43 changes: 43 additions & 0 deletions src/glsl/intensity-frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
uniform sampler2D tDiffuse;
uniform int u_radius;
uniform float u_intensity;
uniform vec2 u_scale;
varying vec2 f_uv;

const int MAXRAD = 10;

float intensity(vec4 color) {
return (color.x + color.y + color.z) / 3.0;
}
// tDiffuse is a special uniform sampler that THREE.js will bind the previously rendered frame to

void main() {
vec4 color = texture2D(tDiffuse, f_uv);


for (int i = -MAXRAD; i < MAXRAD; i++) {
if (i < -u_radius || i > u_radius) continue;
for (int j = -MAXRAD; j < MAXRAD; j++) {
if (j < -u_radius || j > u_radius) continue;
color = texture2D(tDiffuse, f_uv + u_scale * vec2(i,j)) * 255.0;
int curInt = int(intensity(color) * u_intensity / 255.0);
if (curInt > 255) curInt = 255;
intensities[curInt]++;
reds[curInt] += color.r;
greens[curInt] += color.g;
blues[curInt] += color.b;
}
}


int curMax = 0;
int maxIndex = 0;
for (int i = 0; i < 256; i++) {
if (intensities[i] > curMax) {
curMax = intensities[i];
maxIndex = i;
}
}
vec3 rgb = vec3(reds[maxIndex], greens[maxIndex], blues[maxIndex]) / (255.0 * float(curMax));
gl_FragColor = vec4(rgb, 1.0);
}
29 changes: 29 additions & 0 deletions src/glsl/iridescent-frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

uniform sampler2D texture;
uniform vec3 u_albedo;
uniform vec3 u_ambient;
uniform vec3 u_lightPos;
uniform vec3 u_lightCol;
uniform float u_lightIntensity;
uniform vec3 u_viewPos;

varying vec3 f_position;
varying vec3 f_normal;
varying vec2 f_uv;
varying float noise;

// some standard noise function
float rand(vec2 coord){
return fract(sin(dot(coord.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

void main() {
vec4 color = vec4(u_albedo, 1.0);

float d = clamp(dot(f_normal, normalize(u_lightPos - f_position)), 0.0, 1.0);

// color = texture2D(texture, vec2(f_uv.x - d * float(noise), f_uv.y - d * float(noise)));
color = texture2D(texture, f_uv);

gl_FragColor = vec4(d * color.rgb * u_lightCol * u_lightIntensity + u_ambient, 1.0);
}
Loading