Giter Club home page Giter Club logo

shadr's Introduction

shadr

Lifecycle: experimental

shadr provides an interface to write GLSL shaders in R!

Installation

Installation is a bit tricky, as it requires GLFW and GLEW. In all cases, you’ll need RTools and CMake installed. On macOS and Ubuntu the process is fairly straightforward. For Windows 10 instructions it’s a bit more complex–skip down to the Windows 10 section.

For macOS and Ubuntu, first download GLFW from either the GLFW website or via this link. If you’re installing via Brew on macOS, skip down to those instructions (no need to download).

Now, unzip the folder and open it up in the terminal. Run the following:

macOS:

cd glfw-3.3.2
cmake CMakeLists.txt
sudo make install

Now, GLFW should be installed on macOS.

Installing GLEW is straightforward. Download the source code, unzip, and run make:

cd glew-2.1.0
sudo make install

Hopefully, you should be good to go. Try installing the package–if it doesn’t work, clone the repo, open up the project, change the src/Makevars file to the following:

CXX_STD = CXX11
PKG_LIBS = -lglfw3 -lGLEW -framework Cocoa

To install GLFW/GLEW via brew, run the following:

brew install glfw
brew install glew

And change Makevars to the following:

CXX_STD = CXX11
PKG_LIBS = -L/usr/local/lib -lglfw -lGLEW -framework Cocoa

Now, clean and rebuild in the package.

Ubuntu

On Ubuntu 20.04, I also had to install several libraries before installing GLFW:

sudo apt-get install -y libxrandr-dev
sudo apt-get install -y libxinerama-dev
sudo apt-get install -y libxcursor-dev
sudo apt-get install -y libxi-dev
cd glfw-3.3.2
cmake CMakeLists.txt
sudo make install

For GLEW, the process was slightly more involved: First, download the source code, unzip, and run make:

cd glew-2.1.0
sudo make install

I then had to manually link the shared object in /usr/lib (your system configuration may differ):

sudo ln -s /usr/lib64/libGLEW.so.2.1 /usr/lib/libGLEW.so.2.1

Windows 10

For Windows 10, we are going to download the pre-compiled binaries for GLFW and GLEW. Note: I was only able to get this to work for the 64-bit version of R, in all cases. I’ll update these instructions once I figure out what’s wrong with the 32-bit installation.

GFLW: Link to page with pre-compiled binaries. Direct link for 64-bit binaries Direct link for 32-bit binaries GLEW: Link to page with pre-compiled binaries. Direct link to binaries

Unzip the files into the directory of your choice.

First, clone this repo:

git clone https://github.com/tylermorganwall/shadr

You will now need to change the src/Makevars.win file to point to the pre-compiled binaries. There are three variables at the top of the file. Change them to point to the directories you just downloaded and unzipped on your filesystem (I have left my directories in the file so you can see an example):

BASE_DIR_GLEW = C:/Users/tyler/Documents/glew-2.1.0-win32/glew-2.1.0
BASE_DIR_GLFW64 = C:/Users/tyler/Documents/glfw-3.3.2.bin.WIN64/glfw-3.3.2.bin.WIN64
BASE_DIR_GLFW32 = C:/Users/tyler/Documents/glfw-3.3.2.bin.WIN32/glfw-3.3.2.bin.WIN32

Now open the shadr.proj file in the shadr directory to open RStudio, select “Clean and Rebuild” in the Build menu, and the package will be installed.

Compiling on Windows 10

If the above process doesn’t work for you, you will need to compile GLEW and GLFW yourself. First, download the source files. You will also need to add C:/Rtools/mingw_64/bin/ (or wherever Rtools is) to your PATH environment variable. Navigate to the GLFW source folder in a terminal and run the following:

cd glfw-3.3.2
mkdir build
cd build
cmake -G"MinGW Makefiles" ..
mingw32-make.exe

Do the same for GLEW, but first go to the glew-2.1.0/build/cmake directory before running the following:

mkdir build
cd build
cmake -G"MinGW Makefiles" ..
mingw32-make.exe

Now, go into Makevars.win and replace everything with the following:

CXX_STD = CXX11
PKG_LIBS = -lglfw3 -lglew32 -lgdi32 -lopengl32

Now open the shadr.proj file in the shadr directory to open RStudio, select “Clean and Rebuild” in the Build menu, and the package will be installed. Hopefully.

Examples

If you haven’t played with shaders before, check out The Book of Shaders (no affiliation) for a nice intro tutorial. Then check out Shadertoy for a wide variety of samples.

Let’s start with a bouncing ball:

library(shadr)

fragmentshader = "#version 330 core
uniform vec2 u_resolution;
uniform float u_time;
out vec3 color;

vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    st.x += cos(u_time*10)/10;
    st.y += sin(u_time*10)/10;
  float pct = 1.0;
  vec2 toCenter = vec2(0.5)-st;
  pct = smoothstep(length(toCenter)-0.2,0.1 * abs(sin(u_time*4.0)),0.8);
  color = mix(colorA,colorB,pct);
}"

#This just runs the shader--to generate the gif, we'll run generate_shader_movie()
run_shader(fragmentshader, width=800,height=800) 

That just ran the shader on your system–to generate a movie, we’ll run generate_shader_movie() (the following chunk is not evaluated, since the output is the same as above):

#Generate a mp4:
generate_shader_movie(fragmentshader, filename = "output.mp4",
                      width=800,height=800) 

#Generate a gif (decreasing the number of frames since gifs are larger):
generate_shader_movie(fragmentshader, filename = "output.gif", 
                      frames=60, timestep = pi/180*6,
                      width=500,height=500) 

Rendering and animating a signed distance field:

fragmentshader = "#version 330 core
uniform vec2 u_resolution;
uniform float u_time;
out vec3 color;

void main(){
  vec2 st = gl_FragCoord.xy/u_resolution.xy;
  st.x *= u_resolution.x/u_resolution.y;
  float d = 0.0;

  // Remap the space to -1. to 1.
  st = st *2.0-1.0;

  // Make the distance field
  d = length( abs(st)-.3 );

  // Visualize the distance field
  color = vec3(smoothstep(fract(d*abs(sin(u_time))*10.0),0.2,0.8));
}"

run_shader(fragmentshader, width=800,height=800) 

We can also take a single snapshot at a specified time with generate_shader_snapshot().

par(mfrow=c(2,2))
generate_shader_snapshot(fragmentshader, width=800,height=800, time=pi/8)
generate_shader_snapshot(fragmentshader, width=800,height=800, time=2*pi/8)
generate_shader_snapshot(fragmentshader, width=800,height=800, time=3*pi/8)
generate_shader_snapshot(fragmentshader, width=800,height=800, time=4*pi/8)

Here’s a complex one: “Seascape” by Alexander Alekseev aka TDM - 2014 (License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License), translated for shadr.

fragmentshader = "#version 330 core
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
out vec3 color;

const int NUM_STEPS = 8;
const float PI      = 3.141592;
const float EPSILON = 1e-3;
#define EPSILON_NRM (0.1 / u_resolution.x)
//#define AA

// sea
const int ITER_GEOMETRY = 3;
const int ITER_FRAGMENT = 5;
const float SEA_HEIGHT = 0.6;
const float SEA_CHOPPY = 4.0;
const float SEA_SPEED = 0.8;
const float SEA_FREQ = 0.16;
const vec3 SEA_BASE = vec3(0.0,0.09,0.18);
const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6)*0.6;
#define SEA_TIME (1.0 + u_time * SEA_SPEED)
const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);

// math
mat3 fromEuler(vec3 ang) {
    vec2 a1 = vec2(sin(ang.x),cos(ang.x));
    vec2 a2 = vec2(sin(ang.y),cos(ang.y));
    vec2 a3 = vec2(sin(ang.z),cos(ang.z));
    mat3 m;
    m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
    m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
    m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
    return m;
}
float hash( vec2 p ) {
    float h = dot(p,vec2(127.1,311.7)); 
    return fract(sin(h)*43758.5453123);
}
float noise( in vec2 p ) {
    vec2 i = floor( p );
    vec2 f = fract( p );    
    vec2 u = f*f*(3.0-2.0*f);
    return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ), 
                     hash( i + vec2(1.0,0.0) ), u.x),
                mix( hash( i + vec2(0.0,1.0) ), 
                     hash( i + vec2(1.0,1.0) ), u.x), u.y);
}

// lighting
float diffuse(vec3 n,vec3 l,float p) {
    return pow(dot(n,l) * 0.4 + 0.6,p);
}
float specular(vec3 n,vec3 l,vec3 e,float s) {    
    float nrm = (s + 8.0) / (PI * 8.0);
    return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
}

// sky
vec3 getSkyColor(vec3 e) {
    e.y = (max(e.y,0.0)*0.8+0.2)*0.8;
    return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1;
}

// sea
float sea_octave(vec2 uv, float choppy) {
    uv += noise(uv);        
    vec2 wv = 1.0-abs(sin(uv));
    vec2 swv = abs(cos(uv));    
    wv = mix(wv,swv,wv);
    return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
}

float map(vec3 p) {
    float freq = SEA_FREQ;
    float amp = SEA_HEIGHT;
    float choppy = SEA_CHOPPY;
    vec2 uv = p.xz; uv.x *= 0.75;
    
    float d, h = 0.0;    
    for(int i = 0; i < ITER_GEOMETRY; i++) {        
        d = sea_octave((uv+SEA_TIME)*freq,choppy);
        d += sea_octave((uv-SEA_TIME)*freq,choppy);
        h += d * amp;        
        uv *= octave_m; freq *= 1.9; amp *= 0.22;
        choppy = mix(choppy,1.0,0.2);
    }
    return p.y - h;
}

float map_detailed(vec3 p) {
    float freq = SEA_FREQ;
    float amp = SEA_HEIGHT;
    float choppy = SEA_CHOPPY;
    vec2 uv = p.xz; uv.x *= 0.75;
    
    float d, h = 0.0;    
    for(int i = 0; i < ITER_FRAGMENT; i++) {        
        d = sea_octave((uv+SEA_TIME)*freq,choppy);
        d += sea_octave((uv-SEA_TIME)*freq,choppy);
        h += d * amp;        
        uv *= octave_m; freq *= 1.9; amp *= 0.22;
        choppy = mix(choppy,1.0,0.2);
    }
    return p.y - h;
}

vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {  
    float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0);
    fresnel = pow(fresnel,3.0) * 0.5;
        
    vec3 reflected = getSkyColor(reflect(eye,n));    
    vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12; 
    
    vec3 color = mix(refracted,reflected,fresnel);
    
    float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
    color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
    
    color += vec3(specular(n,l,eye,60.0));
    
    return color;
}

// tracing
vec3 getNormal(vec3 p, float eps) {
    vec3 n;
    n.y = map_detailed(p);    
    n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
    n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
    n.y = eps;
    return normalize(n);
}

float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {  
    float tm = 0.0;
    float tx = 1000.0;    
    float hx = map(ori + dir * tx);
    if(hx > 0.0) return tx;   
    float hm = map(ori + dir * tm);    
    float tmid = 0.0;
    for(int i = 0; i < NUM_STEPS; i++) {
        tmid = mix(tm,tx, hm/(hm-hx));                   
        p = ori + dir * tmid;                   
        float hmid = map(p);
        if(hmid < 0.0) {
            tx = tmid;
            hx = hmid;
        } else {
            tm = tmid;
            hm = hmid;
        }
    }
    return tmid;
}

vec3 getPixel(in vec2 coord, float time) {    
    vec2 uv = coord / u_resolution.xy;
    uv = uv * 2.0 - 1.0;
    uv.x *= u_resolution.x / u_resolution.y;    
        
    // ray
    vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time);    
    vec3 ori = vec3(0.0,3.5,time*5.0);
    vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.14;
    dir = normalize(dir) * fromEuler(ang);
    
    // tracing
    vec3 p;
    heightMapTracing(ori,dir,p);
    vec3 dist = p - ori;
    vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);
    vec3 light = normalize(vec3(0.0,1.0,0.8)); 
             
    // color
    return mix(
        getSkyColor(dir),
        getSeaColor(p,n,light,dir,dist),
        pow(smoothstep(0.0,-0.02,dir.y),0.2));
}

// main
void main( ) {
  vec3 color2 = getPixel(gl_FragCoord.xy, u_time);
    color = vec3(pow(color2,vec3(0.65)));
}"

run_shader(fragmentshader, width=800,height=800) 

shadr's People

Contributors

tylermorganwall avatar

Stargazers

 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.