Skip to content

Instantly share code, notes, and snippets.

@dermotbalson
Created August 28, 2015 00:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dermotbalson/190141f07a5e73bc03a1 to your computer and use it in GitHub Desktop.
Save dermotbalson/190141f07a5e73bc03a1 to your computer and use it in GitHub Desktop.
SBS Lighting
--# Notes
--This project shows how to add different types of lighting to 3D scenes (in this case, a cube)
--The original code was written by spacemonkey. This explanation is by ignatz.
--HOW TO USE THIS PROJECT
--There are a number of tabs at the top. Press on a tab to see its code.
--Work through the tabs from left to right, to see the project develop
--You can run the code in any of the project tabs, by
--1. running the program, and
--2. selecting the tab number using the controls at the upper left of the screen
--The program will remember your selection after that.
--This enables you to work with one tab at a time, make changes and see the effects by running the program
--# Main
--Project: Step by step 3D lighting
--Version: 1.0
--Comments:
--Main
--This code manages which Code tab is run
--it remembers your last choice, and if you select a different one, it runs that instead
local tabs = {}
local fnames = {"setup","draw","touched","collide","orientationChanged","close","restart","keyboard","cleanup"}
local fns = {}
local tabDesc={}
function setup()
for k,v in ipairs(fnames) do --store addresses of key event functions
fns[v] = _G[v]
end
SetupParameters()
RunCode()
end
function SetupParameters()
LastCode=readProjectData("Code") or 1 --load stored tab number
parameter.integer("Choose_a_tab",1,#tabs,LastCode,ShowList) --tab selector
parameter.action("Run selected tab", RunCode)
end
function ShowList()
output.clear()
for i=1,#tabs do
print(i,tabDesc[i])
end
end
--these two functions do all the tab switching magic (thanks to Andrew_Stacey)
function localise(n,d)
if d then tabDesc[n]=d end
local t= {}
setmetatable(t,{__index = _G})
--setfenv(2,t)
tabs[n] = t
return t
end
--change tabs
function RunCode()
output.clear()
saveProjectData("Code",Choose_a_tab)
cleanup()
local t = tabs[Choose_a_tab]
for k,v in ipairs(fnames) do
if t[v] then _G[v] = t[v] else _G[v] = fns[v] end -- overwrite with the new code
end
setup()
end
--default empty function to avoid errors for tabs that don't have it
function cleanup()
end
--# Cube3D
--1
if localise then _ENV=localise(1,"Create cube") end --DELETE this line if you copy this code to another project (it is used to manage tab changes)
--All we'll do in this tab is set up a 3D cube, based on the code from 3D lab demo
function setup()
cube=CreateCube()
--allow user to choose colour, set chosen colour
parameter.color("surfaceColor", color(191,41,85,255),function(c) cube:setColors(c) end)
end
function CreateCube()
local m = mesh()
--vertices for the corners of the cube (stolen from 3d lab)
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
m.vertices = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
--now texture it
-- all the unique texture positions needed
local texvertices = { vec2(0,0),
vec2(1,0),
vec2(0,1),
vec2(1,1) }
-- apply the texture coordinates to each triangle
m.texCoords = {
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
return m
end
function draw()
background(220)
perspective()
camera(-1,1,2,0,0,0) --choose viewing angle so we can see it is 3D
cube:draw()
end
function cleanup()
parameter.clear()
SetupParameters()
end
--# Textures
--2
if localise then _ENV=localise(2,"Add textures") end --DELETE this line if you copy this code to another project (it is used to manage tab changes)
--Now you can choose the cube texture
function setup()
cube=CreateCube()
parameter.color("surfaceColor", color(191,41,85,255),function(c) cube:setColors(c) end)
readImage("Cargo Bot:Starry Background")
-- ******* Add some optional textures for cube
allTextures = {
CAMERA,
"Cargo Bot:Codea Icon",
"Cargo Bot:Starry Background"
}
cameraSource(CAMERA_FRONT)
parameter.integer("Texture",0,#allTextures,3,changeTexture) --allow user to change texture
end
--this runs when the texture changes
function changeTexture()
if Texture > 0 then
cube.texture = allTextures[Texture]
cube:setColors(color(255))
else
cube.texture=nil
cube:setColors(surfaceColor)
end
end
--EVERYTHING BELOW IS UNCHANGED --
function CreateCube()
local m = mesh()
--vertices for the corners of the cube (stolen from 3d lab)
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
m.vertices = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
--now texture it
-- all the unique texture positions needed
local texvertices = { vec2(0,0),
vec2(1,0),
vec2(0,1),
vec2(1,1) }
-- apply the texture coordinates to each triangle
m.texCoords = {
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
if c then m:setColors(c) end
return m
end
function draw()
background(220)
perspective()
camera(-1,1,2,0,0,0) --choose viewing angle so we can see it is 3D
cube:draw()
end
function cleanup()
parameter.clear()
SetupParameters()
end
--# Rotate
--3
if localise then _ENV=localise(3,"Rotate our cube") end --DELETE this line if you copy this code to another project (it is used to manage tab changes)
--This is an interesting way to rotate our cube with our fungers
--it uses modelMatrix, which stores the current world position and rotation settings
--we store these to start with, and every time the user drags across the screen,
--we rotate by the change in x and y, and then store the modelMatrix ready for the next redraw
function setup()
cube=CreateCube()
parameter.color("surfaceColor", color(191,41,85,255),function(c) cube:setColors(c) end)
allTextures = {
CAMERA,
"Cargo Bot:Codea Icon",
"Cargo Bot:Starry Background"
}
cameraSource(CAMERA_FRONT)
parameter.integer("Texture",0,#allTextures,0,changeTexture)
currentModelMatrix=modelMatrix() -- store the initial settings ********* NEW
end
function draw()
background(220)
fontSize(24)
text("Use your fingers to rotate the cube",WIDTH/2,HEIGHT-50)
perspective()
camera(-1,1,2,0,0,0)
modelMatrix(currentModelMatrix) --apply the stored settings **** NEW
--do rotation for touch
if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
currentModelMatrix = modelMatrix() --store the resulting settings for next time
end
cube:draw()
ortho()
fill(0)
text("Use your fingers to rotate the cube",50,HEIGHT-50)
end
--NOTE - when we rotate by the x changes, why do we rotate on the y axis (and vice versa)?
-- The reason is that when you drag your finger left or right, you want the cube to spin left or right.
-- This is the same as if you are standing, and then turn around left or right. You turn around your spine
-- which is vertical, so you are turning around the y axis.
-- Similarly, if we move our finger up or down the screen, we want the cube to spin forward or back
-- imagine a gymnast on the parallel bars, holding the bar with both hands and swinging round the bar
-- think about which way the bar runs - it runs on each side of us, ie along the x axis
-- Another way to think about it is that the x and y axis have sails attached, and
-- the finger movement is like a gust of wind. When it blows left or right along the x axis,
-- it catches the y axis, and turns it round.
-- When it blows up or down, it similarly catches the wind on the x axis, and turns it round
--EVERYTHING BELOW IS UNCHANGED --
--this runs when the texture changes
function changeTexture()
if Texture > 0 then
cube.texture = allTextures[Texture]
cube:setColors(color(255))
else
cube.texture=nil
cube:setColors(surfaceColor)
end
end
function CreateCube()
local m = mesh()
--vertices for the corners of the cube (stolen from 3d lab)
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
m.vertices = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
--now texture it
-- all the unique texture positions needed
local texvertices = { vec2(0,0),
vec2(1,0),
vec2(0,1),
vec2(1,1) }
-- apply the texture coordinates to each triangle
m.texCoords = {
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
if c then m:setColors(c) end
if t then m.texture=t end
return m
end
function cleanup()
parameter.clear()
SetupParameters()
end
--# Shader
--4
if localise then _ENV=localise(4,"Add a simple shader") end --DELETE this line if you copy this code to another project (it is used to manage tab changes)
--Now we'll add a basic shader that does nothing for the moment except choose between using a texture image
--or just a colour (which isn't any different from what we've been doing)
--so we should get the same result as for the previous step
--If you don't understand shaders, look for tutorials on them, because you can't go any further without them
function setup()
cube=CreateCube()
cube.shader=shader(ADSLighting.vertexShader,ADSLighting.fragmentShader) --*** attach shader to mesh
parameter.color("surfaceColor", color(191,41,85,255),function(c) cube:setColors(c) end)
allTextures = {
CAMERA,
"Cargo Bot:Codea Icon",
"Cargo Bot:Starry Background"
}
cameraSource(CAMERA_FRONT)
parameter.integer("Texture",0,#allTextures,0,changeTexture)
currentModelMatrix=modelMatrix()
end
--this runs when the texture changes
function changeTexture()
if Texture > 0 then
cube.texture = allTextures[Texture]
cube:setColors(color(255))
cube.shader.useTexture = true
else
cube.shader.useTexture = false
cube:setColors(surfaceColor)
end
end
--this is a very simple shader that does nothing special - yet
ADSLighting = {
vertexShader = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
vColor = color;
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture;
uniform bool useTexture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
//choose between using a texture or just colours
if (useTexture) {
lowp vec4 curCol = texture2D( texture, vTexCoord);
gl_FragColor =curCol;
}
else gl_FragColor=vColor;
}
]]
}
--EVERYTHING BELOW IS UNCHANGED --
function draw()
background(220)
perspective()
camera(-1,1,2,0,0,0)
modelMatrix(currentModelMatrix) --apply the stored settings
--do rotation for touch
if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
currentModelMatrix = modelMatrix() --store the resulting settings for next time
end
cube:draw()
end
function CreateCube()
local m = mesh()
--vertices for the corners of the cube (stolen from 3d lab)
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
m.vertices = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
--now texture it
-- all the unique texture positions needed
local texvertices = { vec2(0,0),
vec2(1,0),
vec2(0,1),
vec2(1,1) }
-- apply the texture coordinates to each triangle
m.texCoords = {
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
if c then m:setColors(c) end
if t then m.texture=t end
return m
end
function cleanup()
parameter.clear()
SetupParameters()
end
--# Ambient
--5
if localise then _ENV=localise(5,"Add Ambient light") end--DELETE this line if you copy this code to another project (it is used to manage tab changes)
--Here we go with the lighting!
--Ambient light is the general lighting level
--You could call it the minimum light that falls on every surface
--if it is 0, then anything that is not lit will be pitch black
--if it is 1, then everything is fully lit all the time
--As you might guess, it is pretty simple - you just multiply the light level by the ambient factor
--There's just one thing - the ambient light doesn't have to be white, so we specify the light colour
--and everything will be tinted by this colour (see the extra parameter provided for this)
function setup()
cube=CreateCube()
cube.shader=shader(ADSLighting.vertexShader,ADSLighting.fragmentShader)
parameter.color("surfaceColor", color(191,41,85,255),function(c) cube:setColors(c) end)
allTextures = {
CAMERA,
"Cargo Bot:Codea Icon",
"Cargo Bot:Starry Background"
}
cameraSource(CAMERA_FRONT)
parameter.integer("Texture",0,#allTextures,0,changeTexture)
parameter.number("ambient", 0, 1, 0.6) -- level of ambient light (0=dark, 1=light) **** NEW
parameter.color("lightColor", color(255,255,255,255)) --- colour of ambient light ***** NEW
currentModelMatrix=modelMatrix()
end
--this runs when the texture changes
function changeTexture()
if Texture > 0 then
cube.texture = allTextures[Texture]
cube:setColors(color(255))
cube.shader.useTexture = true
else
cube.shader.useTexture = false
cube:setColors(surfaceColor)
end
end
--this is a very simple shader that does nothing special - yet
ADSLighting = {
vertexShader = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
vColor = color;
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture;
uniform bool useTexture;
uniform float vAmbientMaterial; // ******* NEW
uniform vec4 lightColor; // ******* NEW
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
// Ambient color component of vertex ******** NEW FUNCTION
//Ambient light multiplies the normal light by the brightness (vAmbientMaterial)
//and also by the colour of that light (lightColor)
vec4 GetAmbientColor(vec4 texCol)
{
vec4 vAmbientColor;
vAmbientColor.xyz = texCol.rgb * lightColor.rgb * vAmbientMaterial;
vAmbientColor.a = 1.0;
return vAmbientColor;
}
void main()
{
lowp vec4 curCol;
//choose between using a texture or just colours
if (useTexture) {
curCol = texture2D( texture, vTexCoord);
}
else curCol=vColor;
gl_FragColor = GetAmbientColor(curCol);
}
]]
}
function draw()
background(220)
perspective()
camera(-1,1,2,0,0,0)
modelMatrix(currentModelMatrix) --apply the stored settings
--do rotation for touch
if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
currentModelMatrix = modelMatrix() --store the resulting settings for next time
end
cube.shader.vAmbientMaterial = ambient --- ****** NEW
cube.shader.lightColor = lightColor --- ****** NEW
cube:draw()
end
--EVERYTHING BELOW IS UNCHANGED --
function CreateCube()
local m = mesh()
--vertices for the corners of the cube (stolen from 3d lab)
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
m.vertices = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
--now texture it
-- all the unique texture positions needed
local texvertices = { vec2(0,0),
vec2(1,0),
vec2(0,1),
vec2(1,1) }
-- apply the texture coordinates to each triangle
m.texCoords = {
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
if c then m:setColors(c) end
if t then m.texture=t end
return m
end
function cleanup()
parameter.clear()
SetupParameters()
end
--# Diffuse
--6
if localise then _ENV=localise(6,"Add Diffuse light") end --DELETE this line if you copy this code to another project (it is used to manage tab changes)
--There are lots of code changes
--The main changes are to calculate "normals" (see end of CreateCube)
--and in the fragment shader, which has a function to calculate diffuse light
function setup()
cube=CreateCube()
cube.shader=shader(ADSLighting.vertexShader,ADSLighting.fragmentShader)
--set position of light source
cube.shader.vLightPosition = vec4(-.5,1,3,1) -- ****** NEW
parameter.color("surfaceColor", color(191,41,85,255),function(c) cube:setColors(c) end)
allTextures = {
CAMERA,
"Cargo Bot:Codea Icon",
"Cargo Bot:Starry Background"
}
cameraSource(CAMERA_FRONT)
parameter.integer("Texture",3,#allTextures,0,changeTexture)
parameter.number("ambient", 0, 1, 0.6)
parameter.number("diffuse", 0, 1, 1.0) --brightness of light source -- ***** NEW
parameter.color("lightColor", color(255,255,255,255))
currentModelMatrix=modelMatrix()
end
--this runs when the texture changes
function changeTexture()
if Texture > 0 then
cube.texture = allTextures[Texture]
cube:setColors(color(255))
cube.shader.useTexture = true
else
cube.shader.useTexture = false
cube:setColors(surfaceColor)
end
end
--shader follows
ADSLighting = {
vertexShader = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec3 normal; // **** NEW
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying highp vec3 vNormal;
void main()
{
vColor = color;
vNormal = normal; // **** NEW
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture;
uniform bool useTexture;
uniform mat4 mModel; // ****** NEW
uniform float vAmbientMaterial;
uniform float vDiffuseMaterial; // **** NEW
uniform vec4 lightColor;
uniform vec4 vLightPosition; // ****** NEW
varying highp vec3 vNormal; // ***** NEW
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
vec4 GetAmbientColor(vec4 texCol)
{
vec4 vAmbientColor;
vAmbientColor.xyz = texCol.rgb * lightColor.rgb * vAmbientMaterial;
vAmbientColor.a = 1.0;
return vAmbientColor;
}
// Diffuse Color component of vertex ***** NEW
vec4 GetDiffuseColor(vec3 vVertexNormal, vec4 texCol)
{
// Transform the normal from Object to Model space
// we also normalize the vector just to be sure ...
vec4 vTransformedNormal = normalize( mModel * vec4( vVertexNormal, 1 ));
// Get direction of light in Model space
vec4 vLightDirection = normalize( vLightPosition - vTransformedNormal );
// Calculate Diffuse intensity
float fDiffuseIntensity = max( 0.0, dot( vTransformedNormal, vLightDirection ));
// Calculate resulting Color
vec4 vDiffuseColor;
vDiffuseColor.xyz = texCol.rgb * lightColor.rgb * fDiffuseIntensity * vDiffuseMaterial;
vDiffuseColor.a = 1.0;
return vDiffuseColor;
}
void main()
{
lowp vec4 curCol;
//choose between using a texture or just colours
if (useTexture) {
curCol = texture2D( texture, vTexCoord);
}
else curCol=vColor;
vec4 ambientColor = GetAmbientColor(curCol);
vec4 diffuseColor = GetDiffuseColor(vNormal, curCol); // **** NEW
gl_FragColor = ambientColor + diffuseColor; // **** NEW
}
]]
}
function draw()
background(220)
perspective()
camera(-1,1,2,0,0,0)
modelMatrix(currentModelMatrix)
--do rotation for touch
if CurrentTouch.state == MOVING then
rotate(CurrentTouch.deltaX,0,1,0)
rotate(CurrentTouch.deltaY,1,0,0)
currentModelMatrix = modelMatrix()
end
cube.shader.mModel = modelMatrix()
cube.shader.vAmbientMaterial = ambient
cube.shader.vDiffuseMaterial = diffuse -- **** NEW
cube.shader.lightColor = lightColor
cube:draw()
end
function CreateCube()
local m = mesh()
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
m.vertices = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
--now texture it
-- all the unique texture positions needed
local texvertices = { vec2(0,0),
vec2(1,0),
vec2(0,1),
vec2(1,1) }
-- apply the texture coordinates to each triangle
m.texCoords = {
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
--calculate normals for vertices --- ******* NEW below
--this assumes flat surfaces, and hard edges between triangles (which is good for cubes)
local normals = {}
for i=1, #m.vertices,3 do --calculate normal for each set of 3 vertices
local n = (m.vertices[i+1] - m.vertices[i]):cross(m.vertices[i+2] - m.vertices[i])
normals[i] = n --then apply it to all 3
normals[i+1] = n
normals[i+2] = n
end
m.normals=normals --attach to mesh
if c then m:setColors(c) end
if t then m.texture=t end
return m
end
--EVERYTHING BELOW IS UNCHANGED --
function cleanup()
parameter.clear()
SetupParameters()
end
--# Specular
--7
if localise then _ENV=localise(7,"Add Specular light") end --DELETE this line if you copy this code to another project (it is used to manage tab changes)
--Specular light is the reflection from a light source (the bright spot!)
function setup()
cube=CreateCube()
cube.shader=shader(ADSLighting.vertexShader,ADSLighting.fragmentShader)
--set position of light source
cube.shader.vLightPosition = vec4(-.5,1,3,1)
cube.shader.vEyePosition = vec4(0,0,3,1) --- **** NEW
parameter.color("surfaceColor", color(191,41,85,255),function(c) cube:setColors(c) end)
allTextures = {
CAMERA,
"Cargo Bot:Codea Icon",
"Cargo Bot:Starry Background"
}
cameraSource(CAMERA_BACK)
parameter.integer("Texture",3,#allTextures,0,changeTexture)
parameter.number("ambient", 0, 1, 0.6)
parameter.number("diffuse", 0, 1, 1.0)
parameter.number("specular", 0, 1, .8) -- ***** NEW
parameter.color("lightColor", color(255,255,255,255))
currentModelMatrix=modelMatrix()
end
function changeTexture()
if Texture > 0 then
cube.texture = allTextures[Texture]
cube:setColors(color(255))
cube.shader.useTexture = true ---NEW
else
cube.shader.useTexture = false ---NEW
cube:setColors(surfaceColor)
end
end
--shader follows
ADSLighting = {
vertexShader = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec3 normal;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying highp vec3 vNormal;
varying highp vec4 vPosition; // *** NEW
void main()
{
vColor = color;
vNormal = normal;
vTexCoord = texCoord;
vPosition = position; // ---NEW
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture;
uniform bool useTexture;
uniform mat4 mModel;
uniform float vAmbientMaterial;
uniform float vDiffuseMaterial;
uniform vec4 lightColor;
uniform vec4 vLightPosition;
uniform vec4 vEyePosition; // *** NEW
uniform float vSpecularMaterial; // **** NEW
varying highp vec3 vNormal;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying highp vec4 vPosition; // *** NEW
vec4 GetAmbientColor(vec4 texCol)
{
vec4 vAmbientColor;
vAmbientColor.xyz = texCol.rgb * lightColor.rgb * vAmbientMaterial;
vAmbientColor.a = 1.0;
return vAmbientColor;
}
// Diffuse Color component of vertex
vec4 GetDiffuseColor(vec3 vVertexNormal, vec4 texCol)
{
// Transform the normal from Object to Model space
// we also normalize the vector just to be sure ...
vec4 vTransformedNormal = normalize( mModel * vec4( vVertexNormal, 1 ));
// Get direction of light in Model space
vec4 vLightDirection = normalize( vLightPosition - vTransformedNormal );
// Calculate Diffuse intensity
float fDiffuseIntensity = max( 0.0, dot( vTransformedNormal, vLightDirection ));
// Calculate resulting Color
vec4 vDiffuseColor;
vDiffuseColor.xyz = texCol.rgb * lightColor.rgb * fDiffuseIntensity * vDiffuseMaterial;
vDiffuseColor.a = 1.0;
return vDiffuseColor;
}
// Returns the specular component of the color ******* NEW
vec4 GetSpecularColor(vec3 vVertexNormal, vec4 vVertexPosition)
{
// Transform the Vertex and corresponding Normal into Model space
vec4 vTransformedNormal = mModel * vec4( vVertexNormal, 1 );
vec4 vTransformedVertex = mModel * vVertexPosition;
// Get the directional vector to the light and to the camera
// originating from the vertex position
vec4 vLightDirection = normalize( vLightPosition - vTransformedVertex );
vec4 vCameraDirection = normalize( vEyePosition - vTransformedVertex );
// Calculate the reflection vector between the incoming light and the
// normal (incoming angle = outgoing angle)
// We have to use the invert of the light direction because "reflect"
// expects the incident vector as its first parameter
vec4 vReflection = reflect( -vLightDirection, vTransformedNormal );
// Calculate specular component
// Based on the dot product between the reflection vector and the camera
// direction.
//
// hint: The Dot Product corresponds to the angle between the two vectors
// hint: if the angle is out of range (0 ... 180 degrees) we use 0.0
float spec = pow( max( 0.0, dot( vCameraDirection, vReflection )), 32.0 );
return vec4( lightColor.r * spec, lightColor.g * spec, lightColor.b * spec, 1.0 ) * vSpecularMaterial;
}
void main()
{
lowp vec4 curCol;
//choose between using a texture or just colours
if (useTexture) {
curCol = texture2D( texture, vTexCoord);
}
else curCol=vColor;
vec4 ambientColor = GetAmbientColor(curCol);
vec4 diffuseColor = GetDiffuseColor(vNormal, curCol);
vec4 specularColor = GetSpecularColor(vNormal, vPosition); // ***** NEW
gl_FragColor = ambientColor + diffuseColor + specularColor; // **** NEW
}
]]
}
function draw()
background(220)
perspective()
camera(-1,1,2,0,0,0)
modelMatrix(currentModelMatrix) --apply the stored settings
--do rotation for touch
if CurrentTouch.state == MOVING then --only rotate while fingers are moving on the screen
rotate(CurrentTouch.deltaX,0,1,0) --rotate by the x change, on the y axis (see note below)
rotate(CurrentTouch.deltaY,1,0,0) --and by the y change, on the x axis (see note below)
currentModelMatrix = modelMatrix() --store the resulting settings for next time
end
cube.shader.mModel = modelMatrix()
cube.shader.vAmbientMaterial = ambient
cube.shader.vDiffuseMaterial = diffuse
cube.shader.vSpecularMaterial = specular -- **** NEW
cube.shader.lightColor = lightColor
cube:draw()
end
--EVERYTHING BELOW IS UNCHANGED --
function CreateCube()
local m = mesh()
--vertices for the corners of the cube (stolen from 3d lab)
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
m.vertices = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
--now texture it
-- all the unique texture positions needed
local texvertices = { vec2(0,0),
vec2(1,0),
vec2(0,1),
vec2(1,1) }
-- apply the texture coordinates to each triangle
m.texCoords = {
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
--calculate normals for vertices
--this assumes flat surfaces, and hard edges between triangles (which is good for cubes)
local normals = {}
for i=1, #m.vertices,3 do --calculate normal for each set of 3 vertices
local n = (m.vertices[i+1] - m.vertices[i]):cross(m.vertices[i+2] - m.vertices[i])
normals[i] = n --then apply it to all 3
normals[i+1] = n
normals[i+2] = n
end
m.normals=normals --attach to mesh
if c then m:setColors(c) end
if t then m.texture=t end
return m
end
function cleanup()
parameter.clear()
SetupParameters()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment