Sending arbitrary data to a shader
Posted: Wed Dec 19, 2018 5:21 pm
I want to make a voxel raycaster, and to make this I need ability to send arbitrary data to the shader. This is because of uniforms limit and other limitations of GLSL structuring. I tried to make this with VolumeImages, but that does not work. I made a demo to test it, and I found that the result is very weird. Normally, it should look just like example.png:
But instead it looks random. I don't know how to fix it. I guess there is something in ridat function inside the shader, but I'm not sure. Here is the code (use up/down arrows to adjust resolution of the data):
Code: Select all
local shader=[[extern ivec2 size;
extern VolumeImage data;
extern int res;
int d(int i1,int i2){return int(mod(i1,i2));}
int di(int i1,int i2){return int(float(i1)/float(i2));}
int ridat(int realindex,int res,VolumeImage data)
{
int veci=di(realindex,4);
int ivi=d(realindex,4);
ivec3 pos=ivec3(d(veci,res),d(di(veci,res),res),di(di(veci,res),res));
vec3 realpos=vec3(float(pos.x)/float(res)+0.5,float(pos.y)/float(res)+0.5,float(pos.z)/float(res)+0.5);
vec4 c=Texel(data,realpos);
if (ivi==0) {
return int(c.r*255);
} else if (ivi==1) {
return int(c.g*255);
} else if (ivi==2) {
return int(c.b*255);
} else if (ivi==3) {
return int(c.a*255);
} else {
return 128;
}
}
vec4 indexdat(ivec2 pos)
{
int realindex=int((pos.x+pos.y*size.y))*3;
vec4 c=vec4(float(ridat(realindex,res,data))/255,
float(ridat(realindex+1,res,data))/255,
float(ridat(realindex+2,res,data))/255,
1);
return c;
}
vec4 effect(vec4 color,Image img, vec2 uvs,vec2 sp) {
ivec2 pos=ivec2(uvs*size);
vec4 c=indexdat(pos);
return c;
}
]]
love.keyboard.setKeyRepeat(true)
effect=love.graphics.newShader(shader)
local img=love.graphics.newImage(love.image.newImageData(1,1)) -- This is placeholder image, just because shaders don't work good for non-textured polygons.
local function pack(dat,res)
local imgs={}
local function writebyte(luaindex,byte)
local realindex=luaindex-1
local veci=math.floor(realindex/4)
local ivi=realindex%4
local x=veci%res
local y=math.floor(veci/res)%res
local z=math.floor(math.floor(veci/res)/res)+1
imgs[z]=imgs[z] or love.image.newImageData(res,res)
local img=imgs[z]
local r,g,b,a=img:getPixel(x,y)
local deb=false
if ivi==0 then
img:setPixel(x,y,byte/255,g,b,a)if deb then print("Writing to r",realindex,veci,ivi,x,y,z,byte)end
elseif ivi==1 then
img:setPixel(x,y,r,byte/255,b,a)if deb then print("Writing to g",realindex,veci,ivi,x,y,z,byte)end
elseif ivi==2 then
img:setPixel(x,y,r,g,byte/255,a)if deb then print("Writing to b",realindex,veci,ivi,x,y,z,byte)end
elseif ivi==3 then
img:setPixel(x,y,r,g,b,byte/255)if deb then print("Writing to a",realindex,veci,ivi,x,y,z,byte)end
end
end
local imgsize=res*res*res*4
local datsize=#dat
assert(datsize<=imgsize,"Data is too big, please use less data or increase resolution of the VolumeImage")
for k,v in ipairs(dat) do
writebyte(k,v)
end
local img=love.graphics.newVolumeImage(imgs)
img:setFilter("nearest","nearest")
return img
end
-- THIS SECTION IS NOT RELATED TO THE PROBLEM --
local mx,my=0,0
local function getAbsoluteMP(x,y)
local wx,wy=love.window.getPosition()
return love.window.toPixels(x)+wx,love.window.toPixels(y)+wy
end
local amx,amy=getAbsoluteMP(love.mouse.getPosition())
function love.mousemoved(x,y)
amx,amy=getAbsoluteMP(x,y)
end
function love.mousepressed(x,y,k)
local x,y=love.window.getPosition()
local imx,imy=amx,amy
if k==1 then
mx,my=imx-x,imy-y
end
end
function love.update(dt)
local x,y=love.window.getPosition()
local imx,imy=amx,amy
--print(x,y,imx,imy,mx,my)
if love.mouse.isDown(1) then
love.window.setPosition(imx-mx,imy-my)
end
end
local res,size=41,256
function love.keypressed(key)
if key=="up" then
size=size+1
elseif key=="down" then
size=size-1
end
size=math.min(512,math.max(1,size))
if (res-1)^3 >= size^2 then
res=res-1
end
while (res)^3 < size^2 do
res=res+1
end
end
love.window.updateMode(800,600,{borderless=true})
-- END OF NON-PROBLEM-RELATED SECTION --
function love.draw()
love.graphics.setShader(effect)
effect:send("size",{size,size});
effect:send("res",res)
local t={}
for x=0,size-1 do
for y=0,size-1 do
local i=((x+y*size))*3+1
--print(i,i+1,i+2,i+3,i+4)
t[i]=x/size*255
t[i+1]=y/size*255
t[i+2]=math.random()*255
end
end
local t2=pack(t,res)
effect:send("data",t2)
love.graphics.draw(img,0,0,0,800,600)
love.graphics.setShader()
end