Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This is Lua code that can be copied and pasted into a new project on the Codea IOS app. It includes a 3D scene lit by a Phong lighting shader. There are also some useful functions that I've written that can be used to easily add vertices, normals & texture coordinates to meshes; such as: addCube, addEllipse & addCylinder.
--# Main
function setup()
-- Set this to true or false to activate/deactivate the lighting shader
useShader = true
rotX,rotY = 0.2,1.3 -- camera rotation
camDist = 100 -- distance from the camera to origin
camOrigin = vec3(0,0,0) -- point of which the camera orbits
lightColour = color(255, 255, 255, 255)
rX,rY = rotX,rotY -- rX,rY will be animated
m = mesh()
if useShader then
m.shader = shader(Lighting.vert,Lighting.frag)
m.shader.ambientLight = color(0, 0, 0, 255)
m.shader.lightPos = vec4(0,0,50)
m.shader.camPos = vec4(0,0,50)
m.shader.lightCol = lightColour
end
-- Add the vertices, normals & texture coordinates to the mesh
verts = {} -- array of vec3's (vertices)
norms = {} -- array of vec3's (normals)
texC = {} -- array of vec2's (texture coordinates)
addRect(verts,norms, vec3(-1000,-1000,0), 2000,2000)
addCube(verts,norms, vec3(10,10,0), 20,20,20)
addCylinder(verts, norms, {pos = vec3(-15,-15,0), w = 10, h = 10, d = 30, edges = 12}, matrix())
addRectTex(texC, 0,0, 100,100)
for i = 1, 6 do
addRectTex(texC, 0,0, 1,1)
end
for i = 0, 11 do
addRectTex(texC, i/12,0, 1/12,1)
end
addEllipseTex(texC, 0,0, 1,1, 12)
addEllipseTex(texC, 0,0, 1,1, 12)
m.vertices = verts
m.texCoords = texC
m:setColors(255,255,255)
m.normals = norms
m.texture = readImage("Surfaces:Basic Bricks Color") -- mesh texture
axis = mesh() -- x,y,z axis mesh
local av = {}
local ac = {}
addCube(av,{}, vec3(0.5,0,0), 6,0.5,0.5)
local c = color(255,0,0)
for i = 1, 36 do
ac[#ac+1] = c
end
addCube(av,{}, vec3(0,0.5,0), 0.5,6,0.5)
c = color(0,255,0)
for i = 1, 36 do
ac[#ac+1] = c
end
addCube(av,{}, vec3(0,0,0), 0.5,0.5,6)
c = color(0,0,255)
for i = 1, 36 do
ac[#ac+1] = c
end
axis.vertices = av
axis.colors = ac
-- Create point mesh
pointM = mesh()
do
local s = 0.5 -- size of point light
local t,l,b,r,f,u = vec3(0,0,s),vec3(0,-s,0),vec3(s,0,0),vec3(0,s,0),vec3(-s,0,0),vec3(0,0,-s)
pointM.vertices = {
l,t,f,
f,t,r,
r,t,b,
b,t,l,
u,l,f,
u,f,r,
u,r,b,
u,b,l
}
pointM:setColors(lightColour)
end
end
function draw()
if useShader then
-- move the point
lightPos = vec4(math.sin(ElapsedTime)*30,math.cos(ElapsedTime)*30,math.cos(ElapsedTime)*10+20)
m.shader.lightPos = lightPos -- update lightPos in the mesh
end
-- draw the background
background(0, 0, 0, 255)
perspective(90)
-- animate rX and rY to make smooth camera motions
rX = rX + (rotX - rX) * 0.2
rY = rY + (rotY - rY) * 0.2
local camPos = vec4( -- calculate camera position
camDist * math.sin(rY) * math.cos(rX) + camOrigin.x,
camDist * -math.cos(rY) * math.cos(rX) + camOrigin.y,
camDist * math.sin(rX) + camOrigin.z
)
-- setup the oribit camera
camera(
camPos.x, camPos.y, camPos.z,
camOrigin.x,camOrigin.y,camOrigin.z,
0,0,1)
if useShader then
m.shader.camPos = modelMatrix():inverse() * camPos -- update camPos in the shader
end
-- draw the mesh
m:draw()
--axis:draw() -- uncomment this to draw the axis
-- draw the point
if useShader then
translate(lightPos.x,lightPos.y,lightPos.z)
pointM:draw()
end
end
function touched(t)
rotY = rotY + t.deltaX * 0.02
rotX = rotX + t.deltaY * 0.02
if rotX > 1.57 then
rotX = 1.57
elseif rotX < -1.57 then
rotX = -1.57
end
end
--# 3DShapes
--[[ addCube
-Adds a cube to the given vertices v. The floats x,y,z are the back bottom left corner of the cube. w,h,d are the width, height and depth of the cube.
-Adds normals to the array n.
--]]
function addCube(v, n, pos, w,h,d, mat)
mat = mat or matrix()
--local right,back,top = pos.x + w, pos.y + h, pos.z + d
-- create the 8 points of the cube
local blf = pos
local brf = pos + mat * vec3(w,0,0)
local trf = pos + mat * vec3(w,0,d)
local tlf = pos + mat * vec3(0,0,d)
local brb = pos + mat * vec3(w,h,0)
local blb = pos + mat * vec3(0,h,0)
local tlb = pos + mat * vec3(0,h,d)
local trb = pos + mat * vec3(w,h,d)
-- add the 12 triangles to the vertices v
-- add the front face
local ind = #v + 1 -- get the first index of this face
v[ind],v[ind+1],v[ind+2] = blf,brf,trf
v[ind+3],v[ind+4],v[ind+5] = trf,tlf,blf
local nv = mat * vec3(0,-1,0)
for i = 0, 5 do
n[ind+i] = nv
end
-- add the right face
ind = #v + 1 -- get the first index of this face
v[ind],v[ind+1],v[ind+2] = brf,brb,trb
v[ind+3],v[ind+4],v[ind+5] = trb,trf,brf
local nv = mat * vec3(1,0,0)
for i = 0, 5 do
n[ind+i] = nv
end
-- add the back face
ind = #v + 1 -- get the first index of this face
v[ind],v[ind+1],v[ind+2] = brb,blb,tlb
v[ind+3],v[ind+4],v[ind+5] = tlb,trb,brb
local nv = mat * vec3(0,1,0)
for i = 0, 5 do
n[ind+i] = nv
end
-- add the left face
ind = #v + 1 -- get the first index of this face
v[ind],v[ind+1],v[ind+2] = blb,blf,tlf
v[ind+3],v[ind+4],v[ind+5] = tlf,tlb,blb
local nv = mat * vec3(-1,0,0)
for i = 0, 5 do
n[ind+i] = nv
end
-- add the top face
ind = #v + 1 -- get the first index of this face
v[ind],v[ind+1],v[ind+2] = tlf,trf,trb
v[ind+3],v[ind+4],v[ind+5] = trb,tlb,tlf
local nv = mat * vec3(0,0,1)
for i = 0, 5 do
n[ind+i] = nv
end
-- add the bottom face
ind = #v + 1 -- get the first index of this face
v[ind],v[ind+1],v[ind+2] = blb,brb,brf
v[ind+3],v[ind+4],v[ind+5] = brf,blf,blb
local nv = mat * vec3(0,0,-1)
for i = 0, 5 do
n[ind+i] = nv
end
end
--[[ addRect
-Adds 6 vertices to v.
-Adds 6 normals to n.
--]]
function addRect(v, n, pos, w,h, mat)
mat = mat or matrix()
local tr = pos + mat * vec3(w,h,0)
v[#v+1] = pos
v[#v+1] = pos + mat * vec3(w,0,0)
v[#v+1] = tr
v[#v+1] = tr
v[#v+1] = pos + mat * vec3(0,h,0)
v[#v+1] = pos
local normal = mat * vec3(0,0,1)
for i = 1, 6 do
n[#n+1] = normal
end
end
--[[ addRectTex
Adds 6 vec2 texture coordinates to the array t, where each pair of 3 coordinates makes up a triangle, and all 6 make up a rectangle. The rectangle's bottom–left corner is situated at x,y and its width and height are w,h.
--]]
function addRectTex(t, x,y, w,h)
local x2,y2 = x + w, y + h -- find the top–right corner of the rectangle
local ind = #t + 1 -- find the first index of this rectangle
t[ind],t[ind+1],t[ind+2] = vec2(x,y),vec2(x2,y),vec2(x2,y2)
t[ind+3],t[ind+4],t[ind+5] = t[ind+2],vec2(x,y2),t[ind]
end
--[[ addEllipse
-Adds vertices to the array v that make up an ellipse.
-Adds normals to the array n.
--]]
function addEllipse(v, n, pos, w,h, edges, mat)
mat = mat or matrix()
local normal = mat * vec3(0,0,1)
local points = {}
for i = 0, edges - 1 do
local ang = i/edges*math.pi*2
local p = pos + mat * vec3(math.cos(ang) * w/2,math.sin(ang) * h/2,0)
points[i+1] = p
end
for i = 2, edges - 1 do
v[#v+1] = points[i]
v[#v+1] = points[i+1]
v[#v+1] = points[1]
n[#n+1] = normal
n[#n+1] = normal
n[#n+1] = normal
end
end
--[[ addEllipseTex
-Adds texture coordinates to t that match the vertices given by addEllipse. the bottom left corner of the bounding box of the circle is x,y. the width and height of the circle is w & h. edges is the number of points around the circle.
--]]
function addEllipseTex(t, x,y, w,h, edges)
local points = {}
for i = 0, edges - 1 do
local ang = i/edges*math.pi*2
local p = vec2(x + w/2 + math.cos(ang) * w/2, y + h/2 + math.sin(ang) * h/2)
points[i+1] = p
end
for i = 2, edges - 1 do
t[#t+1] = points[i]
t[#t+1] = points[i+1]
t[#t+1] = points[1]
end
end
--[[ addCylinder
-Adds vertices to v.
-Adds normals to n.
--]]
function addCylinder(v, n, params, mat)
local edges = params.edges or 8
if params.topCap == nil then
params.topCap = true
end
if params.bottomCap == nil then
params.bottomCap = true
end
mat = mat or matrix()
local topP = {}
local bottomP = {}
for i = 0, edges - 1 do
local ang = i/edges*math.pi*2
local cx = math.cos(ang) * params.w/2
local cy = math.sin(ang) * params.h/2
topP[i+1] = params.pos + mat * vec3(cx,cy,params.d)
bottomP[i+1] = params.pos + mat * vec3(cx,cy,0)
end
for i = 0, edges - 1 do
local a = i+1
local b = (i+1)%edges + 1
v[#v+1] = bottomP[a]
v[#v+1] = bottomP[b]
v[#v+1] = topP[b]
v[#v+1] = topP[b]
v[#v+1] = topP[a]
v[#v+1] = bottomP[a]
local ang1 = i/edges*math.pi*2
local norm1 = mat * vec3(math.cos(ang1),math.sin(ang1),0)
local ang2 = (i+1)/edges*math.pi*2
local norm2 = mat * vec3(math.cos(ang2),math.sin(ang2),0)
n[#n+1] = norm1
n[#n+1] = norm2
n[#n+1] = norm2
n[#n+1] = norm2
n[#n+1] = norm1
n[#n+1] = norm1
end
if params.topCap then
addEllipse(v,n, params.pos + mat * vec3(0,0,params.d), params.w,params.h, edges, mat)
end
if params.bottomCap then
addEllipse(v,n, params.pos, params.w,params.h, edges, mat:rotate(180,1,0,0))
end
end
--# PointLightShader
Lighting = {
vert = [[
uniform mat4 modelViewProjection;
uniform highp vec4 lightPos;
uniform highp vec4 camPos;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec3 normal;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying lowp vec3 vLightDir;
varying lowp vec3 vEyeDir;
varying highp vec3 vNormal;
void main()
{
vColor = color;
vTexCoord = texCoord;
lowp vec3 pos = position.xyz;
vLightDir = lightPos.xyz - pos;
vEyeDir = camPos.xyz - pos;
vNormal = normal;
gl_Position = modelViewProjection * position;
}
]],
frag = [[
precision highp float;
uniform lowp vec4 lightCol;
uniform lowp vec4 ambientLight;
uniform lowp sampler2D texture;
uniform highp sampler2D specularMap;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying lowp vec3 vLightDir;
varying lowp vec3 vEyeDir;
varying highp vec3 vNormal;
void main()
{
vec3 pix = vColor.rgb * texture2D(texture, fract(vTexCoord)).xyz;
vec3 norm = normalize(vNormal);
vec3 lightDir = normalize(vLightDir);
vec3 eyeDir = normalize(vEyeDir);
float d = length(vLightDir);
float spec = 1.0 - texture2D(specularMap, vTexCoord).r;
// Phong lighting
vec3 final =
// texture colour
pix * (
// ambient lighting
ambientLight.xyz +
// lightsource tint * attenuation * (diffuse+specular)
lightCol.rgb * 1. / (1. + 0.0035 * d + 0.000175 * d*d) * (
// diffuse lighting
max(0.,dot(lightDir, norm)) * 0.5 +
// specular lighting
pow(max(0.,dot(normalize(lightDir + eyeDir),norm)),32.) * 0.5
)
);
gl_FragColor = vec4(final,1.0);
}
]]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.