SQL 3d engine (interactive preview)
source link: https://www.tuicool.com/articles/hit/JNZz63j
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
SQL 3d engine (interactive preview)
SQLite query:
preHTML(`WITH RECURSIVE numbers AS ( SELECT 0 AS n UNION ALL SELECT n+1 FROM numbers WHERE n<${Math.max(c.maxCol, c.maxRow)} ), pixels AS ( SELECT rows.n as row, cols.n as col FROM numbers as rows CROSS JOIN numbers as cols WHERE rows.n > ${c.minRow - 1} AND rows.n < ${c.maxRow} AND cols.n > ${c.minCol - 1} AND cols.n < ${c.maxCol} ), rawRays AS ( SELECT row, col, ${(c.x0 - c.cols / 2 * c.scaleX * c.ux - c.rows / 2 * c.scaleY * c.vx).toFixed(4)} + col * ${(c.scaleX * c.ux).toFixed(4)} + row * ${(c.scaleY * c.vx).toFixed(4)} as x, ${(c.y0 - c.rows / 2 * c.scaleY * c.vy).toFixed(4)} + row * ${(c.scaleY * c.vy).toFixed(4)} as y, ${(c.z0 - c.cols / 2 * c.scaleX * c.uz - c.rows / 2 * c.scaleY * c.vz).toFixed(4)} + col * ${(c.scaleX * c.uz).toFixed(4)} + row * ${(c.scaleY * c.vz).toFixed(4)} as z FROM pixels ), normsSq AS ( SELECT row, col, x, y, z, x * x + y * y + z * z AS n2 FROM rawRays ), norms AS ( SELECT row, col, x, y, z, ${sqrt1SQL('n2', '1')} as n FROM normsSq ), rays AS ( SELECT row, col, x / n AS x, y / n AS y, z / n AS z FROM norms ), iters AS ( SELECT row, col, 0 as it, 0 as v FROM rays UNION ALL SELECT rays.row, rays.col, it + 1 AS it, v + ${formula(r.x, r.y, r.z)} AS v FROM iters JOIN rays ON rays.row = iters.row AND rays.col = iters.col WHERE it < ${c.iters} ), lastIters AS ( SELECT it0.row, it0.col, it0.v AS v0, it1.v AS v1, it2.v AS v2 FROM iters as it0 JOIN iters AS it1 ON it0.row = it1.row AND it0.col = it1.col JOIN iters AS it2 ON it0.row = it2.row AND it0.col = it2.col WHERE it0.it = ${c.iters} AND it1.it = ${c.iters - 1} AND it2.it = ${c.iters - 2} ), res AS ( SELECT col, (v0 - v1) / (v1 - v2) as v FROM lastIters ) SELECT group_concat( substr('${ascii}', round(1 + max(0, min(${ascii.length - 1}, v * ${ascii.length}))), 1) || CASE WHEN col = ${c.maxCol - 1} THEN X'0A' ELSE '' END , '') FROM res; `)
viewof alpha = slider({ min: -180, max: 180, step: 1, value: -55, title: "alpha", description: "horizontal rotation" })
viewof beta = slider({ min: -90, max: 90, step: 1, value: 30, title: "beta", description: "vertical rotation" })
viewof dist = slider({ min: 0.5, max: 2.5, step: 0.1, value: 1.5, title: "dist", description: "distance to camera" })
viewof fov = slider({ min: 1, max: 100, step: 1, value: 26, title: "fov", description: "camera field of view" })
{ const sqIt = (e, i) => (i + e / i) / 2; const sqrt = 0 ? x => Math.sqrt(x) : x => sqIt(x, sqIt(x, sqIt(x, 1))); const sqrt1a = (x) => (1 + x) / 2; const sqrt1b = (x) => 0.14 + 1.78 * x; const sqrt1 = (x, init=1) => sqIt(x, init); const sqrt2 = (x, init=1) => sqIt(x, sqIt(x, init)); const fun = 1 ? (x, y, z) => Math.max( Math.abs(x) - 0.3, Math.abs(y) - 0.3, Math.abs(z) - 0.3, -sqrt1b(x * x + y * y + z * z) + 0.42 ) : (x, y, z) => Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) - 0.3; const iterCount = c.iters; let res = '\n'; for (let row=c.minRow; row<c.maxRow; row++) { for (let col=c.minCol; col<c.maxCol; col++) { let dist = 0; const x = c.x0 + (col - c.cols / 2) * c.ux * c.scaleX + (row - c.rows / 2) * c.vx * c.scaleY; const y = c.y0 + (row - c.rows / 2) * c.vy * c.scaleY; const z = c.z0 + (col - c.cols / 2) * c.uz * c.scaleX + (row - c.rows / 2) * c.vz * c.scaleY; const n = sqrt1a(x * x + y * y + z * z); const [nx, ny, nz] = [x / n, y / n, z / n]; const I = []; for (let i=0; i<=iterCount; i++) { dist += fun(c.camX + nx * dist, c.camY + ny * dist, c.camZ + nz * dist); I[i] = dist; } const R0 = (I[iterCount] - I[iterCount - 1]) / (I[iterCount - 1] - I[iterCount - 2]); const r = Math.min(1, Math.max(0, R0 + colorShift)) ** gamma; const R = 1 - (I[iterCount - 1] - I[iterCount - 2] < 0.00000000000001 ? 0.09 : Math.max(0, Math.min(1, R0))); const ch = ('' + ascii[Math.round(r * (ascii.length - 1))])[0]; res += ch; // res += R0 > 0.3 ? '#' : ' '; } res += '\n'; }; return preCanvas(res); }
Explanation article (russian): https://habr.com/post/435390/
You may also want to check out the same thing for Excel: https://beta.observablehq.com/@pallada-92/excel-3d-engine-emulator
preHTML(range(80).map(() => '-').join(''))
viewof sqInit = slider({ min: 0.1, max: 1.0, step: 0.01, value: 0.28, title: "sqInit", description: "initial value of square root" })
viewof gamma = slider({ min: 0.0, max: 2.0, step: 0.1, value: 1.0, title: "gamma", description: "gamma correction" })
viewof colorShift = slider({ min: -1.0, max: 1.0, step: 0.1, value: 0, title: "colorShift", description: "color shift" })
c = ({ iters: 15, minRow: 3, maxRow: 40, minCol: 0, maxCol: 80 + 9, rows: 36 + 7, cols: 80 + 10, scaleX: 0.6, scaleY: 1.5, camX: +(dist * cos(alpha) * cos(beta)).toFixed(camPrecision), camY: +(dist * sin(beta)).toFixed(camPrecision), camZ: +(dist * sin(alpha) * cos(beta)).toFixed(camPrecision), ux: -pixelSize * sin(alpha), uz: +pixelSize * cos(alpha), vx: +pixelSize * cos(alpha) * sin(beta), vy: -pixelSize * cos(beta), vz: +pixelSize * sin(alpha) * sin(beta), x0: -cos(alpha) * cos(beta), y0: -sin(beta), z0: -sin(alpha) * cos(beta), })
formula = (x, y, z) => `MAX(ABS(${x}) - 0.3, ABS(${y}) - 0.3, ABS(${z}) - 0.3, -${sqrt1SQL(`((${x}) * (${x}) + (${y}) * (${y}) + (${z}) * (${z}))`, sqInit.toString())} + 0.42)`
Internals
camPrecision = 2
preCanvas = txt => { const ctx = DOM.context2d(width, width); const fontSize = Math.round(width / 60); ctx.font = `${fontSize}px monospace`; ctx.fillStyle = 'black'; txt.split('\n').map((line, no) => ctx.fillText(line, 5, 5 + no * fontSize * 1.4)); return ctx.canvas; }
preHTML = txt => html`<pre>${txt.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')}</pre>`;
cos = x => Math.cos(x * Math.PI / 180)
sin = x => Math.sin(x * Math.PI / 180)
tan = x => Math.tan(x * Math.PI / 180)
rows = 36
pixelSize = tan(fov / 2) / ((rows - 1) / 2)
ascii = `$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^. `
rangeSQL = (name, size) => `${name} AS (SELECT 0 AS v UNION ALL SELECT v+1 FROM ${name} WHERE v<${size})`
sqrt1SQL = (expr, init) => init == 1 ? `(${init} + ${expr}) / 2.0` : `(${init} + ${expr} / ${init}) / 2.0`
sqrt2SQL = (expr, init) => sqrt1SQL(expr, '(' + sqrt1SQL(expr, init) + ')')
sqrt3SQL = (expr, init) => sqrt1SQL(expr, '(' + sqrt1SQL(expr, '(' + sqrt1SQL(expr, init) + ')') + ')')
r = ({ x: `${c.camX}+v*x`, y: `${c.camY}+v*y`, z: `${c.camZ}+v*z`, })
range = n => { const res = []; for (let i=0; i<n; i++) { res.push(i); } return res; }
function recurIter(n) { return `SELECT row, col, nx, ny, nz, v + ${formula(r.x, r.y, r.z)} as v FROM ${n == 0 ? 'iter0' : '(\n' + recurIter(n - 1) + ')\n'}` }
import {slider, select} from "@jashkenas/inputs"
Shorter, but slower version, I don't know, why:
preHTML(`WITH RECURSIVE numbers AS ( SELECT 0 AS n UNION ALL SELECT n+1 FROM numbers WHERE n<${Math.max(c.maxCol, c.maxRow)} ), pixels AS ( SELECT rows.n as row, cols.n as col FROM numbers as rows CROSS JOIN numbers as cols WHERE rows.n > ${c.minRow - 1} AND rows.n < ${c.maxRow} AND cols.n > ${c.minCol - 1} AND cols.n < ${c.maxCol} ), rawRays AS ( SELECT row * 100 + col AS no, ${(c.x0 - c.cols / 2 * c.scaleX * c.ux - c.rows / 2 * c.scaleY * c.vx).toFixed(4)} + col * ${(c.scaleX * c.ux).toFixed(4)} + row * ${(c.scaleY * c.vx).toFixed(4)} as x, ${(c.y0 - c.rows / 2 * c.scaleY * c.vy).toFixed(4)} + row * ${(c.scaleY * c.vy).toFixed(4)} as y, ${(c.z0 - c.cols / 2 * c.scaleX * c.uz - c.rows / 2 * c.scaleY * c.vz).toFixed(4)} + col * ${(c.scaleX * c.uz).toFixed(4)} + row * ${(c.scaleY * c.vz).toFixed(4)} as z FROM pixels ), normsSq AS ( SELECT no, x, y, z, x * x + y * y + z * z AS n2 FROM rawRays ), norms AS ( SELECT no, x, y, z, ${sqrt1SQL('n2', '1')} as n FROM normsSq ), rays AS ( SELECT no, x / n AS x, y / n AS y, z / n AS z FROM norms ), iters AS ( SELECT no, 0 as it, 0 as v FROM rays UNION ALL SELECT rays.no, it + 1 AS it, v + ${formula(r.x, r.y, r.z)} AS v FROM iters JOIN rays ON rays.no = iters.no WHERE it < ${c.iters} ), lastIters AS ( SELECT it0.no, it0.v AS v0, it1.v AS v1, it2.v AS v2 FROM iters as it0 JOIN iters AS it1 ON it0.no = it1.no JOIN iters AS it2 ON it0.no = it2.no WHERE it0.it = ${c.iters} AND it1.it = ${c.iters - 1} AND it2.it = ${c.iters - 2} ), res AS ( SELECT no, (v0 - v1) / (v1 - v2) as v FROM lastIters ) SELECT group_concat( substr('${ascii}', round(1 + max(0, min(${ascii.length - 1}, v * ${ascii.length}))), 1) || CASE WHEN no % 1000 = 0 THEN X'0A' ELSE '' END , '') FROM res; `)
Recommend
-
7
We are thrilled to bring you a new preview of Wave Egine. New features WebGL improvements. The fourth preview comes with a lot of changes in the WebGL implementation. We have focused...
-
52
Rider for Unreal Engine is now available as Public Preview, with a general release aimed for 2021. Today we are happy to share a new 2020.3.3 update that is packed with lots of long-awa...
-
3
Explore eBay’s New Optimized Spark SQL Engine for Interactive AnalysisSkip to main content ...
-
9
Transform 2021 Live now: AI/ML Automation Technology Summit July 12-16
-
4
Join the fastest growing open-source analytics project Find out more how they all use Trino! Why Trino?
-
9
POLARIS: The Distributed SQL Engine in Azure Synapse这篇paper介绍了Microsoft Azure云在去年年底新发布的一款湖仓一体的大数据分析产品Synapse,而这篇paper介绍的就是他的分布式查询处理引擎Polaris。传统的数据分析业务是基于关系型数仓...
-
3
Windows Terminal Preview 1.13 is now live with a new text rendering engine. Windows Terminal is an essential utility if you are prefer command-line interfaces (CLI) and we already know that
-
0
Google Launches a New Cross-Platform Data Storage Engine BigLake in Preview Apr 19, 2022...
-
3
The new build is live right now for the Dev channel
-
3
Interactive Query Service Amazon Athena Introduces New Engine Oct 30, 2022...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK