In Codea4 how does one do the equivalent of img:set(i,j,thiscol) in Codea3?
@John is there an img:set or equivalent in codea v4?
Sorry, I missed this question. There is but it works a little differently than in V3
You have img:setPixel(x, y, color)
and img:setPixel(x, y, r, g, b, a)
as well as img:setValue(x, y, r, g, b, a)
Why the distinction between setPixel()
and setValue()
? setPixel()
assumes values are between 0 and 255 while setValue()
just sets the data directly. To better match how modern GPUs work and to simplify the engine, you need to call img:apply()
when you are finished setting values on your image to have them show up. You can also use img:readback()
to get data back so you can use getPixel(x, y)
and getValue(x, y)
on the latest data. You can also use:
-- Modern
-- Create a random noise image using image:setForEach()
function setup()
img = image(255, 255)
img:setForEach(function(x, y)
return math.random(), math.random(), math.random(), 1
end)
img:apply()
end
function draw()
background(40, 40, 50)
sprite(img, WIDTH/2, HEIGHT/2)
end
And here’s an example of reading back some image data generated by a shader:
-- Modern
function setup()
print("Hello World!")
-- The code below is a bit of overkill but I wanted some noise values
img = image(512, 512)
context.push(img)
-- Use a shader to draw noise values to the image
background(shader.builder():unlit()
:include "codea/noise/noise.glsl" -- include built-in GPU noise library
:material[[
float n = Perlin2D(getUV0() * 10) * 0.5 + 0.5;
material.baseColor = vec4(n, n, n, 1);
]]
:build())
context.pop()
img:readback(true) -- use true to perform readback immediately (normally it takes a frame)
print(img:getValue(255, 255))
end
function draw()
background(40, 40, 50)
sprite(img, WIDTH/2, HEIGHT/2)
end
Why is it so different? The use of apply and readback is an attempt to reflect what’s actually going on. The V3 version kept causing problems as it tries to automatically transfer data back and forth based on the use of set and get functions. This ended up causing several scenarios where users would cause huge performance hits without realising it since they didn’t know that reading and writing data to the GPU requires the entire image to be sent back and forth each time
@John - thanks for the info. I’ve used the V3 version to build points/pixels into an image then used the image as a sprite. Didn’t ever think of using it in any form of animation, thought it would be too hard on the pad hardware.
Looks like in V4 it will be more powerful - does this require metal?
Thanks again.
@John thanks that works.
@John are you sure the texture is mapped correctly to a sphere? There is a black line at the join of the left and right edges of the texture.
Actually, in v3 it didn’t work correctly either with the native spheres. I was obliged to use @LoopSpace ’s pseudomodel sphere to get a correct wrapping.
Hmm, do you have a screenshot example or some simple code to show this?
@John here is my test code:
-- 3d shadows
function setup()
domImage=makeDomImage()
-- domImage=image.read(asset.builtin.Basic.Icon)
scn = scene.default3d()
scn.sky=color(80, 125, 233)
scn.sky=nil
local rig = scn.camera:add(camera.rigs.orbit)
rig.angles.x = 45
rig.angles.y = 45
scn:entity():add(mesh.plane(10,10))
local sphere1 = scn:entity()
sphere1:add(mesh.sphere(2))
sphere1.y = 2
sphere1.material.map=domImage
local sphere2 = scn:entity()
-- sphere2:add(mesh.sphere(1))
sphere2:add(mesh.sphere(2))
sphere2.y = 5
sphere2.x= 3
sphere2.material.map=domImage
scene.main = scn
-- parameter.number("sx",0,100,3, function() sphere2.x=sx end)
end
function makeDomImage()
-- print("makedom ",pmtcol)
local i,j
-- local img=image(255,255)
-- local img=image(128,128)
-- local img=image(512,512)
local img=image(1024,1024)
mushroom_size = 0.23
pmt_size = 0.05
ring_size = 0.07
twelveth = 0.083333
sixth = 0.1666667
p2 = pmt_size * pmt_size
r2 = ring_size * ring_size
print ("img.w, h",img.width,img.height)
for i=1,img.width do
for j=1,img.height do
-- print(pmtcol)
-- local col=vec4(0,0,0,0) --initially transparent
local col=vec4(1,1,1,1) --initially transparent
local phi_prime = i/img.width
local theta = j/img.height
local up = 0.0
if theta < 0.5 then --force theta > 0.5
phi_prime = phi_prime+twelveth
theta = 1.0-theta
up = 100.0
end
phi_prime = math.fmod( phi_prime , sixth )
if phi_prime > twelveth then phi_prime = sixth - phi_prime end
local a =math.sin( theta * 3.1415 )
a = a*a*4.0
local A = a*phi_prime * phi_prime
local tminusp=twelveth - phi_prime
local B = a * tminusp*tminusp
--the theta's of the dom positions are:
-- 0.980875, 1.2706, 1.872738, 2.162463, 2.579597, 3.1415923073180982
local omt= 1.0-theta
local d1 = omt*omt
local tm8= theta-0.82111139
d1 =math.min( d1, up + A + tm8*tm8 )
local tm7= theta-0.687549
local d1 = math.min (d1, B + tm7*tm7 )
local tm6= theta-0.59587
d1 =math.min( d1, A + tm6*tm6 )
-- if ( d1 < r2 ) then col = vec4( 0.95, 0.8, 1.0, 1.0 ) end --ring
if ( d1 < r2 ) then col = vec4( 0.5, 0.5, 0.5, 1.0 ) end --ring
if ( d1 < p2 ) then --pmt
-- print(pmtcol)
-- col=vec4(pmtcol.r,pmtcol.g,pmtcol.b,pmtcol.a)
col = vec4( 0.5, 0.3, 0.1, 1.0 )
end --pmt
-- if ( d1 < p2 ) then col = pmtcol end --pmt
if ( j/img.height < mushroom_size ) then col = vec4( 0.5, 0.5, 0.5, 1.0 ) end --mushroom
-- print(col[1])
col[1]=math.floor (col[1]*255)
col[2]=math.floor (col[2]*255)
col[3]=math.floor (col[3]*255)
col[4]=math.floor (col[4]*255)
thiscol=color(col[1],col[2],col[3],col[4])
-- print(i,j, thiscol)
img:setPixel(i, j, thiscol)
end
end
img:apply()
return img
end
@John just wondering if you got a chance to look into this?
I tried your example and managed to fix it by using img:setPixel(i-1, j-1, thiscol)
, so it’s just the function not correctly offsetting for Lua style indices, leaving a column of black pixels (default values) which causes the apparent seam
I’ll put a fix in for this in the next patch
@John that fixed it-thanks
@John the sphere mesh seems to have ‘waves’ in the image. Any way to smooth that away?
I think this is due to shadow mapping issues, which can be adjusted via some settings
You can get the shadow settings object via scn.sun:get(light).shadowOptions
shadowOptions["size"] = &Light::ShadowOptions::mapSize;
shadowOptions["constantBias"] = &Light::ShadowOptions::constantBias;
shadowOptions["normalBias"] = &Light::ShadowOptions::normalBias;
shadowOptions["far"] = &Light::ShadowOptions::shadowFar;
shadowOptions["nearHint"] = &Light::ShadowOptions::shadowNearHint;
shadowOptions["farHint"] = &Light::ShadowOptions::shadowFarHint;
shadowOptions["polygonOffsetConstant"] = &Light::ShadowOptions::polygonOffsetConstant;
shadowOptions["polygonOffsetSlope"] = &Light::ShadowOptions::polygonOffsetSlope;
Shadow mapping is very complicated and fussy with various settings. A lot of engines have these settings but generally have sensible defaults. I may not have set everything up correctly or might be missing stuff. The above are the settings exposed via the API and so you can check what they are and try some different values to see if you can fix the issue. The most important settings for this particular problem would be constantBias
and normalBias
which effect which regions are considered to be in shadow based on the unprojected shadow map values
@John my first try of constantBias=1 and normalBias=1 seems to fix the issue. As parameter.number does not work in V4, I did not explore other settings.
Cool. Hopefully we’ll have all the parameter stuff working soon