Created
October 9, 2020 22:18
-
-
Save ylegall/9ceef714324eda270fb35c9b51058e78 to your computer and use it in GitHub Desktop.
singularity: code for https://www.instagram.com/p/CGD_mBSH4ml/?utm_source=ig_web_copy_link
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.ygl.openrndr.demos | |
import org.openrndr.application | |
import org.openrndr.color.ColorRGBa | |
import org.openrndr.color.mix | |
import org.openrndr.draw.BlendMode | |
import org.openrndr.draw.DrawPrimitive | |
import org.openrndr.draw.LineCap | |
import org.openrndr.draw.VertexElementType | |
import org.openrndr.draw.renderTarget | |
import org.openrndr.draw.shadeStyle | |
import org.openrndr.draw.vertexBuffer | |
import org.openrndr.draw.vertexFormat | |
import org.openrndr.extra.compositor.compose | |
import org.openrndr.extra.compositor.draw | |
import org.openrndr.extra.compositor.layer | |
import org.openrndr.extra.compositor.post | |
import org.openrndr.extra.fx.blur.FrameBlur | |
import org.openrndr.extra.fx.blur.GaussianBloom | |
import org.openrndr.extra.gui.GUI | |
import org.openrndr.extra.parameters.Description | |
import org.openrndr.extra.parameters.DoubleParameter | |
import org.openrndr.extras.camera.OrbitalCamera | |
import org.openrndr.extras.camera.OrbitalControls | |
import org.openrndr.extras.camera.applyTo | |
import org.openrndr.extras.meshgenerators.sphereMesh | |
import org.openrndr.ffmpeg.VideoWriter | |
import org.openrndr.math.Polar | |
import org.openrndr.math.Spherical | |
import org.openrndr.math.Vector2 | |
import org.openrndr.math.Vector3 | |
import org.openrndr.math.linearstep | |
import org.openrndr.math.mix | |
import org.openrndr.math.smoothstep | |
import org.openrndr.math.transforms.transform | |
import org.ygl.openrndr.utils.BokehDepthBlur | |
import org.ygl.openrndr.utils.ColorMap | |
import org.ygl.openrndr.utils.cubicPulse | |
import org.ygl.openrndr.utils.isolatedWithTarget | |
import org.ygl.openrndr.utils.randomPoint | |
import kotlin.math.PI | |
import kotlin.math.cos | |
import kotlin.math.pow | |
import kotlin.math.sin | |
import kotlin.random.Random | |
private const val WIDTH = 920 | |
private const val HEIGHT = 920 | |
private const val TOTAL_FRAMES = 360 * 4 | |
private const val LOOPS = 3 | |
private const val DELAY_FRAMES = 180 | |
private const val RECORDING = true | |
fun main() = application { | |
configure { | |
width = WIDTH | |
height = HEIGHT | |
} | |
program { | |
var time = 0.0 | |
val rng = Random(4) | |
val minRadius = 60.0 | |
val maxRadius = 320.0 | |
val numParticles = 10000 | |
val colorMap = ColorMap(listOf( | |
"5d0c5e","eb4738","fa9a6a","fad8b0", | |
)) | |
val bgStars = List(512) { drawer.bounds.randomPoint(rng) } | |
val params = @Description("params") object { | |
@DoubleParameter("horizon radius", 0.0, 1.0, precision = 3) | |
var horizonRadius = 0.21 | |
@DoubleParameter("distortion period", 0.0, 10.0, precision = 2) | |
var period = 4.0 | |
@DoubleParameter("distortion shape", 0.0, 4.0, precision = 2) | |
var shape = 2.5 | |
@DoubleParameter("ring size", 0.0, 200.0, precision = 1) | |
var ringSize = 85.0 | |
} | |
val camera = OrbitalCamera( | |
eye = Vector3.fromSpherical(Spherical(0.0, 75.0, maxRadius)), | |
lookAt = Vector3.ZERO, | |
fov = 60.0 | |
) | |
val blur = BokehDepthBlur() | |
val bloom = GaussianBloom() | |
val tempImage = renderTarget(width, height) { colorBuffer(); depthBuffer() } | |
val particle = sphereMesh(radius = 1.4) | |
val particleInstances = vertexBuffer(vertexFormat { | |
attribute("transform", VertexElementType.MATRIX44_FLOAT32) | |
color(4) | |
}, numParticles) | |
class Particle( | |
val timeOffset: Double, | |
val startAngleOffset: Double, | |
val stopAngleOffset: Double, | |
val radius: Double, | |
val axisRotation: Double | |
) | |
val particles = List(numParticles) { i -> | |
val timeOffset = rng.nextDouble() | |
val startAngleOffset = rng.nextDouble() | |
val radiusOffset = rng.nextDouble() | |
val axisRotation = rng.nextDouble(-0.02, 0.02) | |
val radius = mix(minRadius, maxRadius, radiusOffset) | |
val arcLength = mix(3.0, 0.1, 1 - (1 - radiusOffset).pow(3)) | |
Particle(timeOffset, startAngleOffset, startAngleOffset + arcLength, radius, axisRotation) | |
} | |
fun particlePosition(particle: Particle, t: Double): Vector3 { | |
val angle = mix(particle.startAngleOffset, particle.stopAngleOffset, t) | |
val (x, z) = Vector2.fromPolar(Polar(angle * 360, particle.radius)) | |
val y = particle.radius * sin(2 * PI * (particle.axisRotation)) * cos(2 * PI * (angle - particle.startAngleOffset)) | |
return Vector3(x, y, z) | |
} | |
fun update() { | |
time = ((frameCount - 1) % TOTAL_FRAMES) / TOTAL_FRAMES.toDouble() | |
camera.update(deltaTime) | |
val cameraPosition = Spherical(0.0, 72 + 10 * sin(2 * PI * time), maxRadius) | |
camera.setView(camera.lookAt, cameraPosition, camera.fov) | |
particleInstances.put { | |
for (i in particles.indices) { | |
val particle = particles[i] | |
val timeOffset = (particle.timeOffset + time) % 1.0 | |
val pos = particlePosition(particle, timeOffset) | |
val opacity = cubicPulse(0.5, 0.5, timeOffset) | |
val colorOffset = linearstep(minRadius, maxRadius, particle.radius) | |
var color = colorMap[(1 - colorOffset).pow(2)] | |
color = mix(ColorRGBa.BLACK, color, opacity) | |
write(transform { | |
translate(pos) | |
}) | |
write(color) | |
} | |
} | |
} | |
val composite = compose { | |
layer { | |
draw { | |
drawer.isolatedWithTarget(tempImage) { | |
camera.applyTo(drawer) | |
drawer.lineCap = LineCap.ROUND | |
drawer.drawStyle.blendMode = BlendMode.ADD | |
drawer.clear(ColorRGBa.BLACK) | |
drawer.shadeStyle = shadeStyle { | |
vertexTransform = "x_modelMatrix *= i_transform;" | |
fragmentTransform = "x_fill = vi_color;" | |
} | |
drawer.vertexBufferInstances(listOf(particle), listOf(particleInstances), DrawPrimitive.TRIANGLES, numParticles) | |
} | |
drawer.shadeStyle = shadeStyle { | |
fragmentTransform = """ | |
const float PI = 3.14159265359; | |
vec2 pos = vec2(c_boundsPosition.x, 1 - c_boundsPosition.y); | |
vec2 delta = pos - p_center; | |
float dist = length(delta); | |
float factor = clamp((1 - dist / p_radius), 0, 1); | |
factor = pow(factor, p_shape); | |
vec2 offset = normalize(delta) * -factor; | |
//vec2 offset = normalize(delta) * factor * cos(p_period * (1 - factor)); | |
x_fill = texture(p_image, pos + offset); | |
""".trimIndent() | |
parameter("image", tempImage.colorBuffer(0)) | |
parameter("radius", params.horizonRadius) | |
parameter("center", Vector2(0.5, 0.5)) | |
parameter("period", params.period) | |
parameter("shape", params.shape) | |
} | |
drawer.rectangle(drawer.bounds) | |
drawer.shadeStyle = null | |
drawer.fill = ColorRGBa.WHITE | |
drawer.stroke = ColorRGBa.BLUE | |
drawer.strokeWeight = 1.0 | |
drawer.circles(bgStars, 2.3) | |
drawer.fill = ColorRGBa.BLACK | |
drawer.stroke = null | |
drawer.strokeWeight = 2.0 | |
drawer.circle(drawer.bounds.center, params.ringSize) | |
} | |
} | |
layer { | |
draw { | |
camera.applyTo(drawer) | |
drawer.clear(ColorRGBa.TRANSPARENT) | |
drawer.lineCap = LineCap.ROUND | |
drawer.drawStyle.blendMode = BlendMode.ADD | |
drawer.shadeStyle = shadeStyle { | |
vertexTransform = "x_modelMatrix *= i_transform;" | |
fragmentTransform = """ | |
vec4 color = vi_color; | |
color.a *= smoothstep(0.0, 30.0, v_worldPosition.z); | |
//if (v_worldPosition.z < 0.0) { | |
// color = vec4(0); | |
//} | |
x_fill = color; | |
""".trimIndent() | |
} | |
drawer.vertexBufferInstances(listOf(particle), listOf(particleInstances), DrawPrimitive.TRIANGLES, numParticles) | |
} | |
} | |
post(blur) { | |
depthBuffer = tempImage.depthBuffer!! | |
far = camera.far | |
focusScale = 0.1 | |
focusPoint = 0.5 | |
} | |
post(bloom) { | |
sigma = 0.1 | |
shape = 0.1 | |
gain = 1.2 | |
} | |
post(FrameBlur()) { | |
blend = 0.4 | |
} | |
} | |
val videoTarget = renderTarget(width, height) { colorBuffer() } | |
val videoWriter = VideoWriter.create() | |
.size(width, height) | |
.frameRate(60) | |
.output("video/singularity.mp4") | |
if (RECORDING) { | |
videoWriter.start() | |
} else { | |
//extend(OrbitalControls(camera)) | |
extend(GUI()) { | |
add(params) | |
add(blur) | |
add(bloom) | |
} | |
} | |
extend { | |
update() | |
if (RECORDING) { | |
drawer.isolatedWithTarget(videoTarget) { | |
composite.draw(this) | |
} | |
drawer.image(videoTarget.colorBuffer(0)) | |
if (frameCount > DELAY_FRAMES) { | |
videoWriter.frame(videoTarget.colorBuffer(0)) | |
} | |
if (frameCount >= TOTAL_FRAMES * LOOPS + DELAY_FRAMES) { | |
videoWriter.stop() | |
application.exit() | |
} | |
} else { | |
composite.draw(drawer) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment