@ -17,6 +17,9 @@ public final class SimpleRenderer implements Renderer {
private final int maxDepth ;
private final int maxDepth ;
private final double gamma ;
private final double gamma ;
private final boolean parallel ;
private final boolean iterative ;
public static @NotNull Builder builder ( ) {
public static @NotNull Builder builder ( ) {
return new Builder ( ) ;
return new Builder ( ) ;
}
}
@ -29,6 +32,9 @@ public final class SimpleRenderer implements Renderer {
this . samplesPerPixel = builder . samplesPerPixel ;
this . samplesPerPixel = builder . samplesPerPixel ;
this . maxDepth = builder . maxDepth ;
this . maxDepth = builder . maxDepth ;
this . gamma = builder . gamma ;
this . gamma = builder . gamma ;
this . parallel = builder . parallel ;
this . iterative = builder . iterative ;
}
}
@Override
@Override
@ -37,18 +43,39 @@ public final class SimpleRenderer implements Renderer {
throw new IllegalArgumentException ( "sizes of camera and canvas are different" ) ;
throw new IllegalArgumentException ( "sizes of camera and canvas are different" ) ;
}
}
getPixelStream ( camera . getWidth ( ) , camera . getHeight ( ) ) . parallel ( ) . forEach ( pixel - > {
if ( iterative ) {
var y = ( int ) ( pixel > > 32 ) ;
// render one sample after the other
var x = ( int ) pixel ;
for ( int i = 1 ; i < = samplesPerPixel ; i + + ) {
var sample = i ;
var color = Color . BLACK ;
getPixelStream ( camera . getWidth ( ) , camera . getHeight ( ) , parallel ) . forEach ( pixel - > {
for ( int i = 1 ; i < = samplesPerPixel ; i + + ) {
var y = ( int ) ( pixel > > 32 ) ;
var ray = camera . cast ( x , y ) ;
var x = ( int ) pixel ;
var c = getColor ( scene , ray ) ;
var ray = camera . cast ( x , y ) ;
color = Color . average ( color , c , i ) ;
var c = getColor ( scene , ray ) ;
canvas . set ( x , y , Color . average ( canvas . get ( x , y ) , c , sample ) ) ;
} ) ;
}
}
canvas . set ( x , y , Color . gamma ( color , gamma ) ) ;
// apply gamma correction
} ) ;
getPixelStream ( camera . getWidth ( ) , camera . getHeight ( ) , parallel ) . forEach ( pixel - > {
var y = ( int ) ( pixel > > 32 ) ;
var x = ( int ) pixel ;
canvas . set ( x , y , Color . gamma ( canvas . get ( x , y ) , gamma ) ) ;
} ) ;
} else {
// render one pixel after the other
getPixelStream ( camera . getWidth ( ) , camera . getHeight ( ) , parallel ) . forEach ( pixel - > {
var y = ( int ) ( pixel > > 32 ) ;
var x = ( int ) pixel ;
var color = Color . BLACK ;
for ( int i = 1 ; i < = samplesPerPixel ; i + + ) {
var ray = camera . cast ( x , y ) ;
var c = getColor ( scene , ray ) ;
color = Color . average ( color , c , i ) ;
}
canvas . set ( x , y , Color . gamma ( color , gamma ) ) ;
} ) ;
}
}
}
/ * *
/ * *
@ -77,10 +104,11 @@ public final class SimpleRenderer implements Renderer {
* { @return a stream of the pixels in a canvas with the given size } The pixels { @code x } and { @code y } coordinate
* { @return a stream of the pixels in a canvas with the given size } The pixels { @code x } and { @code y } coordinate
* are encoded in the longs lower and upper 32 bits respectively .
* are encoded in the longs lower and upper 32 bits respectively .
* /
* /
private static @NotNull LongStream getPixelStream ( int width , int height ) {
private static @NotNull LongStream getPixelStream ( int width , int height , boolean parallel ) {
return IntStream . range ( 0 , height )
var stream = IntStream . range ( 0 , height )
. mapToObj ( y - > IntStream . range ( 0 , width ) . mapToLong ( x - > ( long ) y < < 32 | x ) )
. mapToObj ( y - > IntStream . range ( 0 , width ) . mapToLong ( x - > ( long ) y < < 32 | x ) )
. flatMapToLong ( Function . identity ( ) ) ;
. flatMapToLong ( Function . identity ( ) ) ;
return parallel ? stream . parallel ( ) : stream ;
}
}
/ * *
/ * *
@ -100,6 +128,8 @@ public final class SimpleRenderer implements Renderer {
private int samplesPerPixel = 100 ;
private int samplesPerPixel = 100 ;
private int maxDepth = 10 ;
private int maxDepth = 10 ;
private double gamma = 2.0 ;
private double gamma = 2.0 ;
private boolean parallel = true ;
private boolean iterative = false ;
public @NotNull Builder withSamplesPerPixel ( int samples ) {
public @NotNull Builder withSamplesPerPixel ( int samples ) {
if ( samples < = 0 ) throw new IllegalArgumentException ( "samples must be positive" ) ;
if ( samples < = 0 ) throw new IllegalArgumentException ( "samples must be positive" ) ;
@ -119,6 +149,16 @@ public final class SimpleRenderer implements Renderer {
return this ;
return this ;
}
}
public @NotNull Builder withParallel ( boolean parallel ) {
this . parallel = parallel ;
return this ;
}
public @NotNull Builder withIterative ( boolean iterative ) {
this . iterative = iterative ;
return this ;
}
public @NotNull SimpleRenderer build ( ) {
public @NotNull SimpleRenderer build ( ) {
return new SimpleRenderer ( this ) ;
return new SimpleRenderer ( this ) ;
}
}