Initial commit

This commit is contained in:
Michael Wain 2022-04-05 14:52:18 +03:00
commit 046ad98e83
18 changed files with 724 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# Project exclude paths
/out/

View File

@ -0,0 +1,13 @@
package com.alterdekim.rendering;
public class AmbientLight extends Light {
public AmbientLight(Vector3 position) {
super(position);
}
@Override
public double calculateBrightness( Vector3 rd, Vector3 ro, Vector3 normal, double d ) {
return 1.0;
}
}

View File

@ -0,0 +1,7 @@
package com.alterdekim.rendering;
public class Camera extends Object3D {
public Camera(Vector3 position) {
super(position);
}
}

View File

@ -0,0 +1,29 @@
package com.alterdekim.rendering;
public class Canvas extends Raymarcher {
private static final long serialVersionUID = 1L;
private Sphere sphere = new Sphere( new Vector3(0,0,0), 1 );
private LambertLight light = new LambertLight(new Vector3(4,2,4));
private double d = 0;
public Canvas() {
init();
}
@Override
public void onStart() {
this.setCameraPosition(new Vector3(0, 0, -3));
sphere.getMaterial().setColor(new MaterialColor(0.3, 0.7, 1.0));
this.add(sphere);
this.add(new Plane(new Vector3(0,0,0)));
this.addLight(light);
}
@Override
public void onRender() {
light.setPosition(new Vector3(4.0*Math.sin(d), 2.0, 4.0 * Math.cos(d)));
d+=0.2;
}
}

View File

@ -0,0 +1,13 @@
package com.alterdekim.rendering;
public class FOGLight extends Light {
public FOGLight(Vector3 position) {
super(position);
}
@Override
public double calculateBrightness( Vector3 rd, Vector3 ro, Vector3 normal, double d ) {
return 1.0 - Utils.clamp( Utils.rangeConvert( d, 0, 3.6, 0, 1 ), 0.0, 1.0 );
}
}

View File

@ -0,0 +1,19 @@
package com.alterdekim.rendering;
public class LambertLight extends Light {
public LambertLight(Vector3 position) {
super(position);
}
@Override
public double calculateBrightness( Vector3 rd, Vector3 ro, Vector3 normal, double d ) {
Vector3 lightDirection = this.calculateLightDirection(rd, ro, normal, d);
return Utils.clamp(lightDirection.dot(normal), 0.0, 1.0);
}
@Override
public Vector3 calculateLightDirection( Vector3 rd, Vector3 ro, Vector3 normal, double d ) {
return new Vector3( this.getX(), this.getY(), this.getZ() ).subtract(rd.multiply(d).sum(ro)).normalize();
}
}

View File

@ -0,0 +1,62 @@
package com.alterdekim.rendering;
public class Light {
private double x;
private double y;
private double z;
public Light( Vector3 position ) {
this.x = position.x;
this.y = position.y;
this.z = position.z;
}
public void setPosition( double x, double y, double z ) {
this.x = x;
this.y = y;
this.z = z;
}
public void setPosition( Vector3 position ) {
this.x = position.x;
this.y = position.y;
this.z = position.z;
}
public Vector3 getPosition() {
return new Vector3( this.x, this.y, this.z );
}
public void setX( double x ) {
this.x = x;
}
public void setY( double y ) {
this.y = y;
}
public void setZ( double z ) {
this.z = z;
}
public double getX() {
return this.x;
}
public double getY() {
return this.y;
}
public double getZ() {
return this.z;
}
public double calculateBrightness( Vector3 rd, Vector3 ro, Vector3 normal, double d ) {
return 0;
}
public Vector3 calculateLightDirection( Vector3 rd, Vector3 ro, Vector3 normal, double d ) {
return new Vector3(0,0,0);
}
}

View File

@ -0,0 +1,17 @@
package com.alterdekim.rendering;
public class Material {
private MaterialColor color;
public Material( MaterialColor color ) {
this.color = color;
}
public MaterialColor getColor() {
return this.color;
}
public void setColor( MaterialColor color ) {
this.color = color;
}
}

View File

@ -0,0 +1,26 @@
package com.alterdekim.rendering;
public class MaterialColor {
private double hue;
private double saturation;
private double brightness;
public MaterialColor( double hue, double saturation, double brightness ) {
this.hue = hue;
this.saturation = saturation;
this.brightness = brightness;
}
public double getHue() {
return this.hue;
}
public double getSaturation() {
return this.saturation;
}
public double getBrightness() {
return this.brightness;
}
}

View File

@ -0,0 +1,88 @@
package com.alterdekim.rendering;
public class Object3D {
private double x;
private double y;
private double z;
private double rotationX = 0;
private double rotationY = 0;
private double rotationZ = 0;
private Material mat = new Material(new MaterialColor(0,0,0));
public Object3D( Vector3 position ) {
this.x = position.x;
this.y = position.y;
this.z = position.z;
}
public void setMaterial( Material mat ) {
this.mat = mat;
}
public Material getMaterial() {
return this.mat;
}
public void setPosition( Vector3 position ) {
this.x = position.x;
this.y = position.y;
this.z = position.z;
}
public void setRotationX( double x ) {
this.rotationX = x;
}
public void setRotationY( double y ) {
this.rotationY = y;
}
public void setRotationZ( double z ) {
this.rotationZ = z;
}
public double getEulerX() {
return this.rotationX;
}
public double getEulerY() {
return this.rotationY;
}
public double getEulerZ() {
return this.rotationZ;
}
public Vector3 getPosition() {
return new Vector3( this.x, this.y, this.z );
}
public void setX( double x ) {
this.x = x;
}
public void setY( double y ) {
this.y = y;
}
public void setZ( double z ) {
this.z = z;
}
public double getX() {
return this.x;
}
public double getY() {
return this.y;
}
public double getZ() {
return this.z;
}
public double calculateSDF(Vector3 position) {
return 0;
}
}

View File

@ -0,0 +1,31 @@
package com.alterdekim.rendering;
public class Octahedron extends Object3D {
private double size;
public Octahedron(Vector3 position, double size) {
super(position);
this.size = size;
}
public double getSize() {
return this.size;
}
@Override
public double calculateSDF( Vector3 position ) {
if( this.getEulerX() != 0 ) {
position = position.cross(Utils.rotateX(Math.toRadians(this.getEulerX())));
}
if( this.getEulerY() != 0 ) {
position = position.cross(Utils.rotateY(Math.toRadians(this.getEulerY())));
}
if( this.getEulerZ() != 0 ) {
position = position.cross(Utils.rotateZ(Math.toRadians(this.getEulerZ())));
}
position = new Vector3(Math.abs(position.x), Math.abs(position.y), Math.abs(position.z));
return (position.x+position.y+position.z-size)*0.57735027;
}
}

View File

@ -0,0 +1,12 @@
package com.alterdekim.rendering;
public class Plane extends Object3D {
public Plane(Vector3 position) {
super(position);
}
@Override
public double calculateSDF( Vector3 position ) {
return (position.y *(-1)) + 1.0 + this.getY();
}
}

View File

@ -0,0 +1,170 @@
package com.alterdekim.rendering;
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import javax.swing.JPanel;
public class Raymarcher extends JPanel {
private static final long serialVersionUID = 1L;
private final int MAX_STEPS = 80;
private final double MAX_DIST = 100.0;
private final double SURF_DIST = 0.001;
private final Camera camera = new Camera(new Vector3(0,0,0));
private ArrayList<Object3D> objects = new ArrayList<Object3D>();
private ArrayList<Light> lights = new ArrayList<Light>();
public void init() {
Runnable task = new Runnable() {
public void run() {
while(true) {
repaint();
}
}
};
onStart();
Thread thread = new Thread(task);
thread.start();
}
public void onStart() {}
public void onRender() {}
private sceneResult raymarch(Vector3 ro, Vector3 rd) {
double depth = 0;
Material mat = new Material(new MaterialColor(0,0,0));
for( int i = 0; i < MAX_STEPS; i++ ) {
Vector3 p = rd.multiply(depth).sum(ro);
sceneResult res = sceneSDF( p );
double dS = res.dist;
mat = res.mat;
depth += dS;
if( dS < SURF_DIST || depth > MAX_DIST ) {
break;
}
}
return new sceneResult( depth, mat );
}
private sceneResult sceneSDF( Vector3 pos ) {
double min = MAX_DIST;
Material mat = new Material(new MaterialColor(0,0,0));
for( int i = 0; i < this.objects.size(); i++ ) {
double d = this.objects.get(i).calculateSDF(pos);
if( d < min ) {
min = d;
mat = this.objects.get(i).getMaterial();
}
}
return new sceneResult(min, mat);
}
public void setCameraPosition( Vector3 position ) {
this.camera.setPosition(position);
}
public void setCameraRotation( Vector3 rotation ) {
this.camera.setRotationX(rotation.x);
this.camera.setRotationY(rotation.y);
this.camera.setRotationZ(rotation.z);
}
public Camera getCamera() {
return this.camera;
}
public void add( Object3D object ) {
this.objects.add(object);
}
public void remove( Object3D object ) {
this.objects.remove(object);
}
public ArrayList<Object3D> children() {
return this.objects;
}
public void addLight( Light light ) {
this.lights.add(light);
}
public void removeLight( Light light ) {
this.lights.remove(light);
}
public ArrayList<Light> childrenLight() {
return this.lights;
}
private Vector3 calcNormal( Vector3 pos ) {
Vector3 xyy = new Vector3(SURF_DIST,0,0);
Vector3 yxy = new Vector3(0,SURF_DIST,0);
Vector3 yyx = new Vector3(0,0,SURF_DIST);
Vector3 nor = new Vector3(
sceneSDF(pos.sum(xyy)).dist - sceneSDF(pos.subtract(xyy)).dist,
sceneSDF(pos.sum(yxy)).dist - sceneSDF(pos.subtract(yxy)).dist,
sceneSDF(pos.sum(yyx)).dist - sceneSDF(pos.subtract(yyx)).dist);
return nor.normalize();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
onRender();
Vector3 ro = new Vector3( camera.getX(), camera.getY(), camera.getZ() );
double minres = Math.min(getWidth(), getHeight());
for( int x = 0; x < getWidth(); x++ ) {
for( int y = 0; y < getHeight(); y++ ) {
Vector3 rd = new Vector3((((double)x) - 0.5* ((double) getWidth())) / minres, (((double)y) - 0.5* ((double) getHeight())) / minres, 1);
if( camera.getEulerX() != 0 ) {
rd = rd.cross(Utils.rotateX(Math.toRadians(camera.getEulerX())));
}
if( camera.getEulerY() != 0 ) {
rd = rd.cross(Utils.rotateY(Math.toRadians(camera.getEulerY())));
}
if( camera.getEulerZ() != 0 ) {
rd = rd.cross(Utils.rotateZ(Math.toRadians(camera.getEulerZ())));
}
rd = rd.normalize();
sceneResult res = raymarch( ro, rd );
double d = res.dist;
if( d < MAX_DIST ) {
double dif = 0;
Vector3 p = rd.multiply(d).sum(ro);
Vector3 normal = calcNormal(p);
for( int i = 0; i < this.lights.size(); i++ ) {
dif += this.lights.get(i).calculateBrightness(rd, ro, normal, d);
dif = Utils.clamp( dif, 0.0, 1.0 );
}
g.setColor(Color.getHSBColor( (float)res.mat.getColor().getHue(), (float)res.mat.getColor().getSaturation(), (float)dif ));
} else {
g.setColor(Color.BLACK);
}
g.fillRect(x, y, 1, 1);
}
}
}
private class sceneResult {
public double dist;
public Material mat;
public sceneResult( double dist, Material mat ) {
this.dist = dist;
this.mat = mat;
}
}
}

View File

@ -0,0 +1,40 @@
package com.alterdekim.rendering;
import java.awt.EventQueue;
import javax.swing.JFrame;
import java.awt.BorderLayout;
public class RenderingWindow {
private JFrame frmRendering;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
RenderingWindow window = new RenderingWindow();
window.frmRendering.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public RenderingWindow() {
initialize();
}
private void initialize() {
frmRendering = new JFrame();
frmRendering.setTitle("Rendering");
frmRendering.setResizable(false);
frmRendering.setBounds(100, 100, 400, 400);
frmRendering.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Canvas panel = new Canvas();
frmRendering.getContentPane().add(panel, BorderLayout.CENTER);
}
}

View File

@ -0,0 +1,29 @@
package com.alterdekim.rendering;
public class Sphere extends Object3D {
private double radius;
public Sphere( Vector3 position, double radius ) {
super( position );
this.radius = radius;
}
public double getRadius() {
return this.radius;
}
@Override
public double calculateSDF( Vector3 position ) {
if( this.getEulerX() != 0 ) {
position = position.cross(Utils.rotateX(Math.toRadians(this.getEulerX())));
}
if( this.getEulerY() != 0 ) {
position = position.cross(Utils.rotateY(Math.toRadians(this.getEulerY())));
}
if( this.getEulerZ() != 0 ) {
position = position.cross(Utils.rotateZ(Math.toRadians(this.getEulerZ())));
}
return position.subtract(this.getPosition()).length() - this.getRadius();
}
}

View File

@ -0,0 +1,48 @@
package com.alterdekim.rendering;
public class Triangle extends Object3D {
private Vector3 a;
private Vector3 b;
private Vector3 c;
public Triangle(Vector3 position, Vector3 a, Vector3 b, Vector3 c) {
super(position);
this.a = a;
this.b = b;
this.c = c;
}
@Override
public double calculateSDF(Vector3 p) {
if( this.getEulerX() != 0 ) {
p = p.cross(Utils.rotateX(Math.toRadians(this.getEulerX())));
}
if( this.getEulerY() != 0 ) {
p = p.cross(Utils.rotateY(Math.toRadians(this.getEulerY())));
}
if( this.getEulerZ() != 0 ) {
p = p.cross(Utils.rotateZ(Math.toRadians(this.getEulerZ())));
}
Vector3 ba = b.subtract(a); Vector3 pa = p.subtract(a);
Vector3 cb = c.subtract(b); Vector3 pb = p.subtract(b);
Vector3 ac = a.subtract(c); Vector3 pc = p.subtract(c);
Vector3 nor = ba.cross(ac);
return Math.sqrt(
(Math.signum(ba.cross(nor).dot(pa)) +
Math.signum(cb.cross(nor).dot(pb)) +
Math.signum(ac.cross(nor).dot(pc))<2.0)
?
Math.min( Math.min(
dot2(ba.multiply(Utils.clamp(ba.dot(pa)/dot2(ba),0.0,1.0)).subtract(pa)),
dot2(cb.multiply(Utils.clamp(cb.dot(pb)/dot2(cb),0.0,1.0)).subtract(pb)) ),
dot2(ac.multiply(Utils.clamp(ac.dot(pc)/dot2(ac),0.0,1.0)).subtract(pc)) )
:
nor.dot(pa)*nor.dot(pa)/dot2(nor) );
}
private double dot2( Vector3 v ) {
return v.dot(v);
}
}

View File

@ -0,0 +1,47 @@
package com.alterdekim.rendering;
import java.util.ArrayList;
public class Utils {
public static double rangeConvert( double value, double leftMin, double leftMax, double rightMin, double rightMax ) {
double leftSpan = leftMax - leftMin;
double rightSpan = rightMax - rightMin;
double valueScaled = (value - leftMin) / (leftSpan);
return rightMin + (valueScaled * rightSpan);
}
public static double clamp( double value, double MIN_VALUE, double MAX_VALUE ) {
return (value > MAX_VALUE ? MAX_VALUE : value < MIN_VALUE ? MIN_VALUE : value);
}
public static ArrayList<Vector3> rotateX(double theta) {
double c = Math.cos(theta);
double s = Math.sin(theta);
ArrayList<Vector3> v = new ArrayList<Vector3>();
v.add(new Vector3(1, 0, 0));
v.add(new Vector3(0, c, -s));
v.add(new Vector3(0, s, c));
return v;
}
public static ArrayList<Vector3> rotateY(double theta) {
double c = Math.cos(theta);
double s = Math.sin(theta);
ArrayList<Vector3> v = new ArrayList<Vector3>();
v.add(new Vector3(c, 0, s));
v.add(new Vector3(0, 1, 0));
v.add(new Vector3(-s, 0, c));
return v;
}
public static ArrayList<Vector3> rotateZ(double theta) {
double c = Math.cos(theta);
double s = Math.sin(theta);
ArrayList<Vector3> v = new ArrayList<Vector3>();
v.add(new Vector3(c, -s, 0));
v.add(new Vector3(s, c, 0));
v.add(new Vector3(0, 0, 1));
return v;
}
}

View File

@ -0,0 +1,71 @@
package com.alterdekim.rendering;
import java.util.ArrayList;
public class Vector3 {
public double x;
public double y;
public double z;
public Vector3( double x, double y, double z ) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3 subtract( Vector3 s ) {
return new Vector3( this.x - s.x, this.y - s.y, this.z - s.z );
}
public Vector3 multiply( double num ) {
return new Vector3( this.x * num, this.y * num, this.z * num );
}
public Vector3 divide( double num ) {
return new Vector3( this.x / num, this.y / num, this.z / num );
}
public Vector3 sum( Vector3 s ) {
return new Vector3( this.x + s.x, this.y + s.y, this.z + s.z );
}
public double dot( Vector3 s ) {
return (this.x * s.x) + (this.y * s.y) + (this.z * s.z);
}
public Vector3 normalize() {
Vector3 v3 = new Vector3(0,0,0);
double length = Math.sqrt( this.x*this.x + this.y*this.y + this.z*this.z );
if (length != 0) {
v3.x = this.x/length;
v3.y = this.y/length;
v3.z = this.z/length;
}
return v3;
}
public double length() {
return Math.sqrt( this.x*this.x + this.y*this.y + this.z*this.z );
}
public Vector3 cross( Vector3 c ) {
return new Vector3( (this.y * c.z) - (this.z * c.y),
(this.z * c.x) - (this.x * c.z),
(this.x * c.y) - (this.y * c.x));
}
public Vector3 cross( ArrayList<Vector3> matrix ) {
Vector3 result = new Vector3( this.x, this.y, this.z );
result.x = (matrix.get(0).x * this.x) + (matrix.get(0).y * this.y) + (matrix.get(0).z * this.z);
result.y = (matrix.get(1).x * this.x) + (matrix.get(1).y * this.y) + (matrix.get(1).z * this.z);
result.z = (matrix.get(2).x * this.x) + (matrix.get(2).y * this.y) + (matrix.get(2).z * this.z);
return result;
}
public Vector3 reflect( Vector3 normal, Vector3 v ) {
return v.subtract(normal.multiply(2).cross(v).cross(normal));
}
}