{"id":18905963,"url":"https://github.com/sealinesun/four","last_synced_at":"2026-03-05T01:30:23.541Z","repository":{"id":138764724,"uuid":"496340660","full_name":"sealinesun/four","owner":"sealinesun","description":null,"archived":false,"fork":false,"pushed_at":"2022-05-25T18:09:08.000Z","size":3947,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-31T10:52:49.973Z","etag":null,"topics":["cmake","lua"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sealinesun.png","metadata":{"files":{"readme":"README.md","changelog":"History.txt","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-25T18:08:42.000Z","updated_at":"2022-05-29T17:15:18.000Z","dependencies_parsed_at":null,"dependency_job_id":"64ce73a2-e3b5-413a-9f05-f325ff09139a","html_url":"https://github.com/sealinesun/four","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sealinesun%2Ffour","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sealinesun%2Ffour/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sealinesun%2Ffour/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sealinesun%2Ffour/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sealinesun","download_url":"https://codeload.github.com/sealinesun/four/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239889026,"owners_count":19713702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cmake","lua"],"created_at":"2024-11-08T09:14:15.189Z","updated_at":"2026-03-05T01:30:23.487Z","avatar_url":"https://github.com/sealinesun.png","language":"Lua","readme":"four [![Build Status](https://travis-ci.org/lubyk/four.png)](https://travis-ci.org/lubyk/four)\n===\n\nLightweight OpenGL rendering engine. Basic abstraction layer for\ncompositional GLSL shader programming and rendering.\n\n[Documentation](http://doc.lubyk.org/four.html).\n\ninstall\n-------\n\n    luarocks install four\n\n\nFour\n====\n\nFour is a lightweight OpenGL rendering engine for Lubyk. It provides a\nbasic abstraction layer for compositional GLSL shader programming and\nrendering.\n\nBelow, a quick tour of four is provided, more details can be found in\nthe library's reference documentation generated from the Lua\nfiles. Basic knowledge of OpenGL and 3D real-time rendering is\nassumed.\n\n\nQuick theory of operations\n--------------------------\n\nFour works under an implicit OpenGL rendering context. Setting up the\nOpenGL context is left to the client of the library. Currently it\nneeds at least an OpenGL 3.2 context. New rendering backends\n(e.g. OpenGL ES) can be added by implementing the `Renderer.lua`\ninterface; `RendererGL32.lua` can be used as a blueprint.\n\nTo get something rendered on the screen the following steps must be taken.\n\n1. Create a renderer object `renderer` with the appropriate backend \n   (defaults to OpenGL 3.2).\n2. Make sure there's a corresponding valid OpenGL context. \n3. Invoke `renderer:render(cam, {obj})` where `cam` is a camera object \n   and `obj` a renderable. \n\nA *renderable* is any object with two mandatory fields, `geometry` and\n`effect` respectively referencing a `Geometry` and `Effect` object;\nmore on this in the following sections.\n\nA minimal example drawing a tri-colored triangle can be found in\n[`test/minimal.lua`](test/minimal.lua), run it with:\n\n    luajit minimal.lua \n\n\nMathematical conventions\n------------------------\n\nThe following conventions are used throughout the library.\n\n* In 3D space we assume a \n  [right-handed](http://mathworld.wolfram.com/Right-HandedCoordinateSystem.html)\n  coordinate system.\n* Angles are always given in radians. \n* In 2D space positive angles determine counter clockwise rotations. \n* In 3D space positive angles determine rotations directed according to the \n  [right-hand](http://mathworld.wolfram.com/Right-HandRule.html) rule. \n\n\nBasic graphics data types\n-------------------------\n\nFour provides data types and functions for vectors `V2`, `V3`, `V4`,\n4x4 matrices `M4`, and quaternions `Quat`. Even though these types are\nimplemented as Lua arrays use them as abstract, immutable, types.\n\nThere is no special type for color values but the `Color` module\nprovides convenience constructor and accessors to specify colors in\nthe HSVA or RGBA color spaces. The resulting colors are stored as RGBA\nintensities in `V4` values.\n\n\nTransforms\n----------\n\nA `Transform` object is a convenience mutable object to orient an\nobject in 3D space. It decomposes an `M4` matrix into scaling,\nrotation and translation components (applied in this order) available\nthrough these keys:\n\n* `scale`, the `V3` value defining the scaling component.\n* `rot`, the `Quat` value defining the orientation component. \n* `pos`, the `V3` value defining the translation component. \n* `matrix`, the `M4` matrix resulting from scaling, rotating and translating\n   according to `scale`, `rot` and `pos`.\n\nThe `scale`, `rot` and `pos` keys can be set directly; this\nautomatically updates `matrix` and *vice-versa*.\n\nTransform objects can be attached to renderables and cameras via their \n`transform` key.\n\n\nBuffers\n-------\n\nA `Buffer` object holds 1D to 4D integer or float vectors in a linear\nLua array (future versions of four should also allow to wrap a\nmalloc'd C pointer).\n\nBuffers are used to specify vertex data and texture data. The important \n`Buffer` keys are:\n\n* `dim`, the vector dimension.\n* `scalar_type`, the vector's element type, defines how the \n  data will be stored on the GPU. \n\nThe following code defines a buffer with three 3D vertices. \n\n```lua\nlocal vs = four.Buffer { dim = 3, scalar_type = four.Buffer.FLOAT } \nvs:push3D(-0.8, -0.8, 0.0)\nvs:push3D( 0.8, -0.8, 0.0)\nvs:pushV3(four.V3(0.0,  0.8, 0.0))\n\nassert(vs:length() == 3) \nassert(vs:scalar_length() == 9)\n```\n\nBy default a buffer's data is disposed once it is uploaded on the GPU\nby the renderer. This can be prevented by setting the `disposable` key\nto `false`. In that case also consider setting the `update` key to an\nappropriate value.\n\n**WARNING** Buffer indexing is one-based for now. Four will rapidly\nchange to zero-based indexing for buffers because OpenGL indexes are\nzero-based and having to deal with the two forms simultaneously\nis error-prone. \n\nGeometries\n----------\n\nA `Geometry` object gathers vertex data buffers, an index buffer and a\nprimitive --- lines, triangles, triangle strips, etc. \n\nThe index buffer indexes **with zero-based indices** into vertex data\nbuffers. Along with the primitive this specifies renderable geometry\nfor the GPU.\n\nThe important `Geometry` keys are:\n\n* `data`, table of named buffer objects all of the same length\n  defining per vertex data. The key names are used to bind to \n  the corresponding vertex shader inputs.\n* `primitive`, indicates how the index buffer should be interpreted to\n  specify the geometry. For example with `Geometry.TRIANGLES`,\n  the indices in the `index` buffer are taken three by three to define\n  triangles. \n* `index`, a buffer of **unsigned** ints or bytes of any dimension, \n  indexing with zero-based indices into the buffers of `data` to define \n  the actual sequence of primitives. \n\nThe following function returns a `Geometry` object for a colored\ntriangle located in clip space:\n\n```lua\nfunction triangle () -- Geometry object for a triangle inside clip space\n  local vs = Buffer { dim = 3, scalar_type = Buffer.FLOAT } \n  local cs = Buffer { dim = 4, scalar_type = Buffer.FLOAT }\n  local is = Buffer { dim = 3, scalar_type = Buffer.UNSIGNED_INT }\n  \n  vs:push3D(-0.8, -0.8, 0.0)      -- Vertices\n  vs:push3D( 0.8, -0.8, 0.0)\n  vs:push3D( 0.0,  0.8, 0.0)\n\n  cs:pushV4(four.Color.red ())    -- Vertices' colors\n  cs:pushV4(four.Color.green ())\n  cs:pushV4(four.Color.blue ())\n\n  is:push3D(0, 1, 2)              -- Index for a single triangle\n\n  return Geometry { primitive = Geometry.TRIANGLES, \n                    index = is, data = { vertex = vs, color = cs}}\nend\n```\n\nTo access the geometry's data in a vertex shader the GLSL code must\ndeclare variables whose names match the keys of the `data` table:\n\n```glsl\nin vec3 vertex; // vertex buffer \nin vec3 color;  // color buffer\n```\n\nNote that once a geometry object was rendered its buffer structure ---\nthat is the buffer objects used --- cannot change, the underlying data\nin the buffers may, however, change.\n\n\nEffects\n-------\n\nAn effect object defines a configuration of the GPU for rendering a\ngeometry object. The important keys of an effect are:\n\n* `vertex`, the vertex shader.\n* `geometry`, the geometry shader (optional).\n* `fragment`, the fragment shader. \n* `default_uniforms`, a key/value table defining default values for uniforms. \n* `uniforms`, a uniform lookup function invoked to get uniform values. \n\n\nShaders fields `vertex`, `geometry` and `fragment` are either\n`Effect.Shader` objects or a list thereof. An `Effect.Shader` object\nonly wraps a piece of GLSL code, for example `Effect.Shader(src)` is a\nshader object with the GLSL code `src`.\n\nThe following effect colors geometry specified in clip space.\n```lua\nlocal effect = Effect -- Colors the triangle\n{\n  vertex = Effect.Shader [[\n    in vec3 vertex;\n    in vec3 color;\n    out vec4 v_color;\n    void main()\n    {\n      v_color = vec4(color, 1.0);\n      gl_Position = vec4(vertex, 1.0);\n    }]],\n  \n   fragment = Effect.Shader [[\n     in vec4 v_color;\n     out vec4 color;\n     void main() { color = v_color; }\n   ]]\n}\n```\n\nIf a shader field holds a table of shader objects, those are\nconcatenated to form the shader source, this allows to define and\nreuse shader functions from other modules.\n\n\n### Uniforms lookup\n\nShaders may declare uniforms variables. When an effect `e` is used the\nactual value bound to the uniform is determined as follows. Given an\nuniform named `u`:\n\n```glsl\nuniform vec4 u;\n```\n\nthe function call `e.uniform(e, cam, r, \"u\")` is invoked where `e` is\nthe effect, `cam` the current camera, `r` the renderable. If the\nfunction returns `nil`, the result of `e.default_uniforms[\"u\"]` is\nused.\n\nThe default implementation of `e.uniform` is `return r[\"u\"]`, that is\nit looks for a corresponding key in the renderable.\n\nTODO document the map from lua types to GLSL types.\n\n### Special uniforms values\n\nThe effect module defines a few special uniform values. These\nvalues are dynamically computed by the renderer according to the\ncurrent camera and renderable (or other parameters). \n\nFor example the special uniform value `Effect.MODEL_TO_CLIP`\nautomatically holds the matrix for transforming from model space to\nclip space according to the current renderable transform, camera\ntransform and projection.\n\nHere is a typical vertex shader transforming vertices from model space\nto clip space:\n```lua\nlocal effect = Effect \n{\n  default_uniforms = { m2c = Effect.MODEL_TO_CLIP } \n  vertex = Effect.Shader [[ \n    uniform mat4 m2c;\n    in vec3 vertex; \n    void main () { gl_Position = m2c * vec4(vertex, 1.0); } \n  ]]\n}\n```\n\nRenderables\n-----------\n\nA renderable can be any object. To be rendered by the renderer it must\nat least have an `effect` and a `geometry` object, otherwise it is\nsimply ignored. The following keys are interpreted by the renderer:\n\n* `geometry`, the geometry object to render. \n* `effect`, the effect with which `geometry` should be rendered. \n* `transform` (optional), a transform object defining a transform to \n  apply to `geometry`, in other words, a world transform.  \n* `instance_count`, the number of instances to render. \n* `visible` (optional), if present and `false` disables the rendering\n  of the renderable.\n \n\nCamera\n------\n\nA `Camera` object defines a view volume of world space. Only those\nobject that are part of the view volume are rendered. The important\nkeys:\n\n* `transform` defines the location and orientation of the camera. The\n  default transform lies at the origin and looks down the z-axis. \n* `range`, a `V2` value defining the near and far clip plane as a distance\n  along the forward vector from the current point of view.\n* `fov`, the horizontal field of view. \n* `aspect`, the camera width/height ratio.\n\nThe following code defines a camera located at `cam_pos` and looking\nat the point `cam_target`.\n\n```lua\nlocal cam_pos = V3(-3, 5, 5) \nlocal cam_target = V3(1, 1, 1) \nlocal cam_rot = Quat.rotMap(-V3.oz(), V3.unit(cam_target - cam_pos))\nlocal cam = Camera { transform = Transform { pos = cam_pos, rot = cam_rot }}\n```\n\nTODO implement and use lookat.\n\nMultipass rendering and opacity partitionning\n---------------------------------------------\n\nGiven a renderer, a camera `cam` and a list of renderables `rs`, a frame \nis rendered with: \n```lua\nrenderer:render(cam, rs) \n```\nTo minimize GPU configuration changes, the renderables in `rs` are sorted \nand rendered according to the effect they use. \n\nRenderables can be rendered with multiple pass. In fact an effect can\nbe either an effect as defined above or a list of effects. In general\nthis results in a tree structure. The depth-first order traversal of\nthis tree defines a number of passes. The renderer start by rendering\nthe first pass of each renderable, then the second pass etc.\n\nIn each pass effects are partitionned into opaque and non-opaque\neffects according to the `opaque` boolean attribute of an effect. \nRenderables with opaque effects are rendered before the ones\nwith non-opaque effects.\n\nTODO examples\n\nTroubleshooting tips\n--------------------\n\nTODO expand and improve\n\n### Nothing is drawn\n\nFor a renderable `r` verify the following \n\n* `assert(r.visible == nil or r.visible)`\n* `assert(r.effect)`\n* `assert(r.geometry)`\n* `assert(r.geometry.index.type == UNSIGNED_INT ||\n          r.geometry.index.type == UNSIGNED_BYTE)`\n\n### GL error: invalid enum\n\nEnsure that `geometry.primitive`, `buffer.type` is not `nil`.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsealinesun%2Ffour","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsealinesun%2Ffour","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsealinesun%2Ffour/lists"}