Skip to content

Instantly share code, notes, and snippets.

@KrabCode
Last active November 27, 2018 09:46
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save KrabCode/733c2b4fdd636378a50283303975d72c to your computer and use it in GitHub Desktop.
Save KrabCode/733c2b4fdd636378a50283303975d72c to your computer and use it in GitHub Desktop.
Tessellated spheres source in IntelliJ IDEA - to run this in the vanilla Processing IDE just remove everything that mentions MainApp including the "class MainApp extends PApplet" part and you're golden - also you'll need the PostFX and PeasyCam libraries found in Tools -> Add tool.
import ch.bildspur.postfx.builder.PostFX;
import peasy.PeasyCam;
import processing.core.PApplet;
import processing.core.PShape;
import processing.core.PVector;
import processing.opengl.PShader;
import java.util.ArrayList;
public class MainApp extends PApplet {
PeasyCam cam;
PostFX fx;
private ArrayList<Particle> particles = new ArrayList<Particle>();
private PShape[] tesselatedSpheresIndexedByDetailLevel;
private ArrayList<PShape> rings = new ArrayList<PShape>();
private float sinTime = 0;
private float linearTime = 0;
private float linearTimeAtCaptureStart = 0;
private float expectedLinearTimeAtCaptureEnd = 0;
private float rot = 0;
private float rotSpd = 0;
private int backgroundColorA;
private int backgroundColorB;
private PVector center;
private float screen;
private float sunRadius;
private int maxSphereDetail = 3;
private int baseShapeRadius = 50;
private int totalFramesToAnimate = 300;
private float rotationMaxSpeed = .8f;
private boolean onPC = true;
private boolean capturingLoopRightNow = false;
private int capturingLoopStartFrame=0;
private PShader phong;
public static void main(String[] args) {
PApplet.main("MainApp");
}
public void settings() {
size(1000,1000,P3D);
smooth(8);
}
public void keyPressed() {
if (key=='k') {
capturingLoopStartFrame = frameCount;
linearTimeAtCaptureStart = linearTime;
expectedLinearTimeAtCaptureEnd = (frameCount+totalFramesToAnimate)*TWO_PI/totalFramesToAnimate;
capturingLoopRightNow = true;
}
}
public void setup() {
frameRate(30);
//the order of 1) cam and 2) fx is important
cam = new PeasyCam(this, 600);
fx = new PostFX(this);
phong = loadShader("PhongFrag.glsl", "PhongVert.glsl");
colorMode(RGB, 255, 255, 255, 100);
ellipseMode(CENTER);
rectMode(CENTER);
backgroundColorA = color(30, 0, 0);
backgroundColorB = color(0, 0, 30);
screen = min(width, height);
center = new PVector(width * .5f, height * .5f);
tesselatedSpheresIndexedByDetailLevel = new PShape[maxSphereDetail];
for (int i = 1; i < maxSphereDetail; i++) {
tesselatedSpheresIndexedByDetailLevel[i] = createSphere(baseShapeRadius, i);
}
//size of the sun in the middle for scale
sunRadius = (screen * .05f) / 2f;
//spheres of rising size forming main ring around the sun
particles.addAll(generateParticleCloud(
500,
screen * .05f,
screen * .225f,
screen * .005f,
screen * .02f,
radians(20),
"sphere"
));
//tiny boxes huge cloud
particles.addAll(generateParticleCloud(
300,
screen * .1f,
screen * .5f,
screen * .003f,
screen * .0031f,
TWO_PI*4,
"box"
));
//obelisks rotating on the margins
particles.addAll(generateParticleCloud(
12,
screen * .3f,
screen * .31f,
screen * .05f,
screen * .05f,
0,
"box", "obelisk", "deterministicRotPos", "noLocalRot"
));
String[] unusedFlags = {"restrictedYDeviation"};
}
public void draw() {
sinTime = sin(frameCount * TWO_PI / totalFramesToAnimate);
linearTime = frameCount * TWO_PI / totalFramesToAnimate;
hint(DISABLE_DEPTH_TEST);
cam.beginHUD();
backgroundGradient();
cam.endHUD();
hint(ENABLE_DEPTH_TEST);
// translate(center.x, center.y, screen / 5.4f);
rotateX(PI * .35f);
rotateZ(rot + 0.244346095f);
lightSpecular(255, 255, 255);
pointLight(255, 255, 255, 0, 0, 0);
shininess(10);
for (Particle particle : particles) {
particle.draw();
}
noLights();
if (onPC) {
resetShader();
}
fill(0);
stroke(150);
strokeWeight(2);
rotateY(linearTime);
shape(tesselatedSpheresIndexedByDetailLevel[2]);
rot += rotSpd;
rotSpd *= .98;
cam.beginHUD();
float bloomIntensity = 6.759259f;
fx.render().bloom(0, floor(bloomIntensity), bloomIntensity)
.chromaticAberration()
.compose();
cam.endHUD();
if(capturingLoopRightNow && frameCount-capturingLoopStartFrame <= totalFramesToAnimate){
saveFrame("test10//##.png");
println(frameCount-capturingLoopStartFrame + "/" +totalFramesToAnimate);
}
}
private ArrayList<Particle> generateParticleCloud(float count, float startR, float endR, float minPSize, float maxPSize, float deviation, String... flags) {
ArrayList<Particle> cloud = new ArrayList<Particle>();
for (int i = 0; i < count; i++) {
float r = map(i, 0, count, startR, endR);
float a = random(TWO_PI);
if (contains("deterministicRotPos", flags)) {
a = map(i, 0, count, 0, TWO_PI);
}
float x = r * cos(a);
float y = r * sin(a);
float d = dist(x, y, 0, 0);
float dN = map(d, startR, endR, 0, 1);
float size = lerp(minPSize, maxPSize, dN);
cloud.add(new Particle(x, y, 0, size, deviation, flags));
}
return cloud;
}
class Particle {
String[] flags;
PVector pos;
PVector localRotPos;
PVector localRotSpd;
float orbitalRotAngle;
float orbitalRotPos;
float r;
float boxW, boxH, boxD;
boolean fill;
boolean isBox;
int sphereDetail;
int foregroundColor;
Particle(float x, float y, float z, float size, float deviation, String... flags) {
this.r = size;
pos = new PVector(x, y, z);
orbitalRotAngle = random(-deviation, deviation);
orbitalRotPos = random(-deviation, deviation);
if(contains("restrictedYDeviation", flags)){
orbitalRotPos = 0;
}
localRotPos = new PVector(random(PI), random(PI), random(PI));
localRotSpd = new PVector(rr(rotationMaxSpeed), rr(rotationMaxSpeed), rr(rotationMaxSpeed));
if (contains("noLocalRot", flags)) {
localRotPos = new PVector();
localRotSpd = new PVector();
}
fill = true;
isBox = random(1) > .5f;
boxW = random(r);
boxH = random(r);
boxD = random(r);
sphereDetail = floor(random(1, maxSphereDetail));
this.flags = flags;
flagCorrection(flags);
}
private void flagCorrection(String[] flags) {
if (contains("sphere", flags)) {
isBox = false;
}
if (contains("box", flags)) {
isBox = true;
boxW = r;
boxH = r;
boxD = r;
if(contains("obelisk", flags)){
boxD *= 3;
}
}
}
void draw() {
if (r <= 0) {
return;
}
resetForeground();
pushMatrix();
rotateY(orbitalRotPos);
rotateX(orbitalRotAngle);
rotateZ(linearTime);
translate(pos.x, pos.y, pos.z);
rotateZ(linearTime);
if (fill) {
fill(0);
noStroke();
ambient(foregroundColor);
specular(foregroundColor);
} else {
noFill();
strokeWeight(1);
stroke(255);
}
if (onPC) {
shader(phong);
}
if (isBox) {
box(boxW, boxH, boxD);
} else {
pushMatrix();
scale(r / baseShapeRadius);
shape(tesselatedSpheresIndexedByDetailLevel[sphereDetail]);
popMatrix();
}
if (particles.indexOf(this) == particles.size() - 1) {
if (contains("noLocalRot", flags)) {
// coords();
}
}
popMatrix();
}
void resetForeground() {
if (particles.size() == 0) {
return;
}
colorMode(HSB, 360, 1, 1, 1);
int i = particles.indexOf(this);
float iN = map(i, 0, particles.size(), 0, 1);
if (contains("deterministicRotPos", flags)) {
iN = 0;
}
float hue = (-linearTime/TWO_PI*360)%360 + iN * 120f;
foregroundColor = color(rybToHsb(hue), 1f, 1f);
colorMode(RGB, 255, 255, 255, 100);
}
}
// - utility methods
private void backgroundGradient() {
noStroke();
pushMatrix();
beginShape();
fill(backgroundColorA);
vertex(0, 0);
vertex(width, 0);
fill(backgroundColorB);
vertex(width, height);
vertex(0, height);
endShape(CLOSE);
popMatrix();
}
public void mouseDragged() {
rotSpd += radians(pmouseX - mouseX) / 120f;
}
private float mx() {
float val = map(mouseX, 0, screen, 0, 1);
println("mx: " + val);
return val;
}
private float my() {
float val = map(mouseY, 0, height, 0, 1);
println("my: " + val);
return val;
}
private void coords() {
pushStyle();
strokeWeight(5);
float mag = 300;
stroke(255, 0, 0);
line(-mag, 0, 0, mag, 0, 0);
stroke(0, 255, 0);
line(0, -mag, 0, 0, mag, 0);
stroke(0, 0, 255);
line(0, 0, -mag, 0, 0, mag);
popStyle();
}
float rr(float a) {
return random(-a, a);
}
boolean contains(String key, String[] set) {
for (String s : set) {
if (s.equals(key)) return true;
}
return false;
}
// - amazing tessellated sphere by nking who helped me a lot on this, thanks!
// https://www.shadertoy.com/user/nking
PShape createSphere(float r, int detail) {
PShape res = createShape();
float deg5 = TWO_PI / 5f;
float deg6 = acos((1f + sqrt(5f)) / (5f + sqrt(5f)));
PVector top = new PVector(0, r, 0);
PVector[] sides = new PVector[5];
for (int i = 0; i < 5; i++) {
sides[i] = new PVector(
r * sin(deg6) * cos(deg5 * i),
r * cos(deg6),
r * sin(deg6) * sin(deg5 * i));
}
res.disableStyle();
res.beginShape(TRIANGLES);
for (int i = 0; i < 5; i++) {
trig(res, r, top, sides[(i + 1) % 5], sides[i], detail);
trig(res, r, sides[i], flip(sides[(i + 3) % 5]), flip(sides[(i + 2) % 5]), detail);
trig(res, r, flip(sides[i]), sides[(i + 2) % 5], sides[(i + 3) % 5], detail);
trig(res, r, flip(top), flip(sides[i]), flip(sides[(i + 1) % 5]), detail);
}
res.endShape();
return res;
}
PVector flip(PVector v) {
return new PVector(-v.x, -v.y, -v.z);
}
void trig(PShape ps, float r, PVector p1, PVector p2, PVector p3, int detail) {
if (detail > 1) {
PVector mid12 = PVector.add(p1, p2);
mid12.setMag(r);
PVector mid23 = PVector.add(p2, p3);
mid23.setMag(r);
PVector mid13 = PVector.add(p1, p3);
mid13.setMag(r);
detail--;
trig(ps, r, p1, mid12, mid13, detail);
trig(ps, r, p2, mid23, mid12, detail);
trig(ps, r, p3, mid13, mid23, detail);
trig(ps, r, mid12, mid23, mid13, detail);
} else {
PVector dir = PVector.add(p1, p2);
dir.add(p3);
normal(ps, dir);
vertex(ps, p1);
vertex(ps, p2);
vertex(ps, p3);
}
}
void vertex(PShape ps, PVector v) {
ps.vertex(v.x, v.y, v.z);
}
void normal(PShape ps, PVector v) {
ps.normal(v.x, v.y, v.z);
}
// - brilliant color correction by sighack
//https://sighack.com/post/procedural-color-algorithms-hsb-vs-ryb
float rybToHsb(float hue) {
hue = (360 - hue) % 360;
hue = hue % 360;
float ryb_hue = 0;
float[][] ryb_wheel = {
{0, 0}, {15, 8}, {30, 17}, {45, 26},
{60, 34}, {75, 41}, {90, 48}, {105, 54},
{120, 60}, {135, 81}, {150, 103}, {165, 123},
{180, 138}, {195, 155}, {210, 171}, {225, 187},
{240, 204}, {255, 219}, {270, 234}, {285, 251},
{300, 267}, {315, 282}, {330, 298}, {345, 329},
{360, 0}
};
for (int i = 0; i < ryb_wheel.length - 1; i++) {
float x0 = ryb_wheel[i][0];
float y0 = ryb_wheel[i][1];
float x1 = ryb_wheel[i + 1][0];
float y1 = ryb_wheel[i + 1][1];
/* Ensure that y1 > y0 */
if (y1 < y0)
y1 += 360;
/* If hue lies between y0 and y1, do linear mapping */
if (hue >= x0 && hue < x1) {
ryb_hue = map(hue, x0, x1, y0, y1) % 360;
break;
}
}
return ryb_hue;
}
}
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-15 The Processing Foundation
Copyright (c) 2004-12 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
uniform int lightCount;
uniform vec4 lightPosition[8];
uniform vec3 lightNormal[8];
uniform vec3 lightAmbient[8];
uniform vec3 lightDiffuse[8];
uniform vec3 lightSpecular[8];
uniform vec3 lightFalloff[8];
uniform vec2 lightSpot[8];
uniform float distance;
in vec4 vAmbient;
in vec4 vSpecular;
in vec4 vEmissive;
in float vShininess;
in vec4 vColor;
in vec3 ecVertex;
in vec3 vEcNormal;
const float zero_float = 0.0;
const float one_float = 1.0;
const vec3 zero_vec3 = vec3(0);
float falloffFactor(vec3 lightPos, vec3 vertPos, vec3 coeff) {
vec3 lpv = lightPos - vertPos;
vec3 dist = vec3(one_float);
dist.z = dot(lpv, lpv);
dist.y = sqrt(dist.z);
return one_float / dot(dist, coeff);
}
float spotFactor(vec3 lightPos, vec3 vertPos, vec3 lightNorm, float minCos, float spotExp) {
vec3 lpv = normalize(lightPos - vertPos);
vec3 nln = -one_float * lightNorm;
float spotCos = dot(nln, lpv);
return spotCos <= minCos ? zero_float : pow(spotCos, spotExp);
}
float lambertFactor(vec3 lightDir, vec3 vecNormal) {
return max(zero_float, dot(lightDir, vecNormal));
}
float blinnPhongFactor(vec3 lightDir, vec3 vertPos, vec3 vecNormal, float shine) {
vec3 np = normalize(vertPos);
vec3 ldp = normalize(lightDir - np);
return pow(max(zero_float, dot(ldp, vecNormal)), shine);
}
void main() {
vec3 ecNormal = normalize(vEcNormal);
vec3 ecNormalInv = ecNormal * -one_float;
vec4 color = vColor;
vec4 ambient = vAmbient;
vec4 specular = vSpecular;
vec4 emissive = vEmissive;
float shininess = vShininess;
// Light calculations
vec3 totalAmbient = vec3(0, 0, 0);
vec3 totalFrontDiffuse = vec3(0, 0, 0);
vec3 totalFrontSpecular = vec3(0, 0, 0);
vec3 totalBackDiffuse = vec3(0, 0, 0);
vec3 totalBackSpecular = vec3(0, 0, 0);
for (int i = 0; i < 8; i++) {
if (lightCount == i) break;
vec3 lightPos = lightPosition[i].xyz;
bool isDir = lightPosition[i].w < one_float;
float spotCos = lightSpot[i].x;
float spotExp = lightSpot[i].y;
vec3 lightDir;
float falloff;
float spotf;
if (isDir) {
falloff = one_float;
lightDir = -one_float * lightNormal[i];
} else {
falloff = falloffFactor(lightPos, ecVertex, lightFalloff[i]);
lightDir = normalize(lightPos - ecVertex);
}
spotf = spotExp > zero_float ? spotFactor(lightPos, ecVertex, lightNormal[i],
spotCos, spotExp)
: one_float;
if (any(greaterThan(lightAmbient[i], zero_vec3))) {
totalAmbient += lightAmbient[i] * falloff;
}
if (any(greaterThan(lightDiffuse[i], zero_vec3))) {
totalFrontDiffuse += lightDiffuse[i] * falloff * spotf *
lambertFactor(lightDir, ecNormal);
totalBackDiffuse += lightDiffuse[i] * falloff * spotf *
lambertFactor(lightDir, ecNormalInv);
}
if (any(greaterThan(lightSpecular[i], zero_vec3))) {
totalFrontSpecular += lightSpecular[i] * falloff * spotf *
blinnPhongFactor(lightDir, ecVertex, ecNormal, shininess);
totalBackSpecular += lightSpecular[i] * falloff * spotf *
blinnPhongFactor(lightDir, ecVertex, ecNormalInv, shininess);
}
}
// Calculating final color as result of all lights (plus emissive term).
// Transparency is determined exclusively by the diffuse component.
vec4 vertColor = vec4(totalAmbient, 0) * ambient +
vec4(totalFrontDiffuse, 1) * color +
vec4(totalFrontSpecular, 0) * specular +
vec4(emissive.rgb, 0);
vec4 backVertColor = vec4(totalAmbient, 0) * ambient +
vec4(totalBackDiffuse, 1) * color +
vec4(totalBackSpecular, 0) * specular +
vec4(emissive.rgb, 0);
gl_FragColor = gl_FrontFacing ? vertColor : backVertColor;
}
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-15 The Processing Foundation
Copyright (c) 2004-12 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
uniform mat4 modelviewMatrix;
uniform mat4 transformMatrix;
uniform mat3 normalMatrix;
uniform int lightCount;
uniform vec4 lightPosition[8];
uniform vec3 lightNormal[8];
uniform vec3 lightAmbient[8];
uniform vec3 lightDiffuse[8];
uniform vec3 lightSpecular[8];
uniform vec3 lightFalloff[8];
uniform vec2 lightSpot[8];
attribute vec4 position;
attribute vec4 color;
attribute vec3 normal;
out vec4 vColor;
attribute vec4 ambient;
attribute vec4 specular;
attribute vec4 emissive;
attribute float shininess;
out vec4 vAmbient;
out vec4 vSpecular;
out vec4 vEmissive;
out float vShininess;
out vec3 ecVertex;
out vec3 vEcNormal;
const float zero_float = 0.0;
const float one_float = 1.0;
const vec3 zero_vec3 = vec3(0);
void main() {
// Vertex in clip coordinates
gl_Position = transformMatrix * position;
// Vertex in eye coordinates
ecVertex = vec3(modelviewMatrix * position);
// Normal vector in eye coordinates
vEcNormal = normalize(normalMatrix * normal);
vColor = color;
vAmbient = ambient;
vSpecular = specular;
vShininess = shininess;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment