Why faking 3D nowadays?

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Why faking 3D nowadays?

Post by pgimeno »

4aiman wrote: Wed Feb 28, 2018 4:32 pm Thanks a lot for your answers!

I guess I should've been more specific, though.
Love3d and other projects I saw here (love2d forums & github) use shaders and/or ffi in order to achieve proper 3d representation. Heck, even raycasters tend to be hardware-dependent (can't find a single raycaster that have textured floor/ceiling AND works on Android).

My interest lies not in love3d (or any other specific lib for that matter) and certainly not in raycasting.
I'm just wondering if there some newly-introduced built-in features of love2d 0.11.x to make perspective textured planes in 3d.

TL;DR
Something like love.graphics.rectangle(x,y,z,width,heigth, length,...) is a distant dream, but is it possible to go 3d without ffi and shaders as of now? Note that I'm talking about a cross-platform solution.
Without shaders, no. Shaders are at the core of Löve. They are always running, even if you don't define any.

To work in 3D with OpenGL, you need a vertex shader. I've looked more into love3d now, and it's now in my to-do list to try and make a raycasting-looking demo using actual GL 3D rendering. The key to obtain it is love.graphics.newMesh and an appropriate vertex and fragment ("pixel") shader, but especially vertex.

Now, take a look at this love3d demo: https://github.com/excessive/love3d-dem ... orward.lua

Pay special attention to how little it uses "l3d" and for what purpose. In that demo, it is used ONLY for two purposes: 1) to provide a depth buffer so that only the closest pixels are drawn, and 2) to not draw polygons that don't point to the camera. The rest is done with pure Löve. For something as simple as the raycasting-like demo I've mentioned earlier, you can do without both, and then you don't need love3d, just pure Löve 0.10. The love3d demo uses the IQM library, which in turn uses functions love.graphics.newMesh and Mesh:setVertexMap to create the mesh. IQM uses FFI to speed up loading of the file format it supports, but according to what I have seen so far, it seems to me that that loading could have been implemented in pure Lua with no problems. I am still studying it.

As I noted in a previous post, Löve 0.11 provides depth buffers, and that makes (1) unnecessary. It also introduces love.graphics.setMeshCullMode, which makes (2) unnecessary. This means that with 0.11, you don't need love3d to do real 3D like in that demo.

Have I answered your question now?

Edit: Simple proof of concept follows. I see no reason for it not to work in mobile. Note it does not use any libraries or FFI, but it uses a (trivial) vertex shader.

Code: Select all

local img, sb

-- Vertex shader code
local shader = [[
extern mat4 view;

vec4 position(mat4 transform, vec4 vertex) {
  // ignore love's transform which is only for 2d and use our own matrix
  return view * vertex;
}
]]

function love.load(args)
  img = love.graphics.newImage('image1.jpg')

  -- A spritebatch is a mesh, so let's use that for simplicity
  sb = love.graphics.newSpriteBatch(img, 64)
  -- Generate 8x8 quads (image is 640x480 which means each quad is 80x60)
  local quads = {}
  for y = 0, 7 do for x = 0, 7 do
    quads[y * 8 + x + 1] = love.graphics.newQuad(x*80, y*60, 80, 60, 640, 480)
  end end
  -- Add some quads at random
  for i = 1, 60 do
    sb:add(quads[love.math.random(1, 64)],
           love.math.random(0, 7) * 80,
           love.math.random(0, 7) * 60)
  end

  -- Compile the shader in place
  shader = love.graphics.newShader(shader)
end

local t = 0
function love.update(dt)
  t = t + dt
end

function love.draw()
  -- Use a preset matrix for demo purposes. A real matrix can be generated
  -- with a 3D math library like CPML.
  if love._version_major > 0 then
    -- love 0.11 requires the matrix in row-major order
    shader:send('view', {
       1.3, 0,    0, -450,
         0, 0, -1.7, -120,
         0, 1,    0,   50,
         0, 1,    0,   50,
    })
  else
    -- send the matrix in column-major order
    shader:send('view', {
       1.3,    0,  0,  0,
         0,    0,  1,  1,
         0, -1.7,  0,  0,
      -450, -120, 50, 50,
    })
  end

  -- Toggle vertex shader on/off every second
  if t >= 2 then
    love.graphics.setShader()
    t = 0
  elseif t >= 1 then
    love.graphics.setShader(shader)
  end

  -- Draw the spritebatch
  love.graphics.draw(sb)
end
Edit2: Oops, wrong image. The new one is 640x480.

Edit3: Updated to 11.0
Attachments
demo3d3.love
updated to 11.0
(69.07 KiB) Downloaded 322 times
demo3d2.love
(68.95 KiB) Downloaded 222 times
demo3d.love
(118.14 KiB) Downloaded 224 times
Last edited by pgimeno on Wed Apr 04, 2018 4:08 pm, edited 2 times in total.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Why faking 3D nowadays?

Post by pgimeno »

Here's something that is a bit more than the simple proof of concept above, but it's still little more than a mini-demo and not usable for anything serious yet, because it doesn't do any sorting: everything is rendered in the order it's created.

It allows you to draw 3D rectangles (actually rhomboids) given three 3D vertices: top left, top right, and bottom left (the bottom right one is calculated) and the UV corners. Everything is done via "the standard way" of using GPU rendering. By that I mean that it uses the same 3D projection method that actual 3D engines use. You don't have to worry about compatibility; the vertex shader is so very close to the Löve default that I'd say it's guaranteed to work on any platform where Löve works.

I've added on-screen buttons for testing in a mobile phone. I've tested in mine and it has no problems with it at all.

Edit: Updated to 11.0
Edit2: I had a typo in the matrix multiplication function, which somehow didn't have any visible effects, but I've fixed it anyway.
Attachments
rctest3.love
fix typo in matrix multiply
(72.16 KiB) Downloaded 474 times
rctest2.love
updated to 11.0
(72.16 KiB) Downloaded 198 times
rctest.love
(72.03 KiB) Downloaded 231 times
Last edited by pgimeno on Thu May 17, 2018 2:00 pm, edited 2 times in total.
Cruhan
Prole
Posts: 4
Joined: Sat Mar 18, 2017 3:45 pm

Re: Why faking 3D nowadays?

Post by Cruhan »

interesting, it's a shame no raycaster engine gets really finished hehe.
PGUp
Party member
Posts: 105
Joined: Fri Apr 21, 2017 9:17 am

Re: Why faking 3D nowadays?

Post by PGUp »

For fun, most of the time, also for game styles
-
User avatar
Davidobot
Party member
Posts: 1226
Joined: Sat Mar 31, 2012 5:18 am
Location: Oxford, UK
Contact:

Re: Why faking 3D nowadays?

Post by Davidobot »

pgimeno wrote: Tue Mar 13, 2018 11:50 pm ...
pgimeno wrote: Sun Mar 18, 2018 10:31 pm ...
Hey! Thanks so much for these examples - they make everything so much clearer. A quick question though - the example break in the recently-released 11.0 and I can't seem to figure out why. Would you be able to fix your wonderful example to work with the latest version of LOVE?
PM me on here or elsewhere if you'd like to discuss porting your game to Nintendo Switch via mazette!
personal page and a raycaster
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Why faking 3D nowadays?

Post by pgimeno »

I think so. I'm sorta busy these days and it may take me some time. I also have Gspöt, GifLoad and T2R in my waiting queue, but this will probably be quick so I'll grab it first.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Why faking 3D nowadays?

Post by pgimeno »

Turns out, Shader:send() sends the matrix in row-major order in 11.0 (thanks shakesoda for the info). I've updated the posts.
User avatar
Davidobot
Party member
Posts: 1226
Joined: Sat Mar 31, 2012 5:18 am
Location: Oxford, UK
Contact:

Re: Why faking 3D nowadays?

Post by Davidobot »

pgimeno wrote: Wed Apr 04, 2018 3:49 pm...
Thanks a bunch! I tried changing the row/column major using the settings mentioned in the wiki, but it didn't work, as mentioned in the other post.

I've been trying to recreate your demos using CPML, but I can't quite the viewport matrix to match to yours. You create yours from scratch, while I use from_perspective. With values like (60, 4/3, 0.0625, 1024.0625) to mimick the ones in your example.

However, I've been unsuccessful.
PM me on here or elsewhere if you'd like to discuss porting your game to Nintendo Switch via mazette!
personal page and a raycaster
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Why faking 3D nowadays?

Post by pgimeno »

Davidobot wrote: Wed Apr 04, 2018 5:56 pm I've been trying to recreate your demos using CPML, but I can't quite the viewport matrix to match to yours. You create yours from scratch, while I use from_perspective. With values like (60, 4/3, 0.0625, 1024.0625) to mimick the ones in your example.

However, I've been unsuccessful.
After from_perspective, you have to pre-multiply the matrix with a rotation of 90° over the Z axis (to make the view face east), and then another rotation of 90° over the X axis (to convert from top view to horizontal view).

I may have messed up the order. By pre-multiply I mean if the result of from_perspective is M and the rotation is R, you have to multiply R * M, not M * R.

Edit: Forgot to say, beware that CPML multiplication is backwards. https://github.com/excessive/cpml/issues/33

Edit 2: Yes I messed up the order. The perspective matrix is the last one to apply in the pre-multiply chain.
Last edited by pgimeno on Sat Apr 07, 2018 12:16 am, edited 1 time in total.
User avatar
Davidobot
Party member
Posts: 1226
Joined: Sat Mar 31, 2012 5:18 am
Location: Oxford, UK
Contact:

Re: Why faking 3D nowadays?

Post by Davidobot »

pgimeno wrote: Wed Apr 04, 2018 6:36 pm...
Doing the premultiplication yields a matrix similar to the one that works, but it still doesn't. I'm not sure if I'm fundamentally misunderstanding something here.

The following code outputs this matrix:

Code: Select all

local axes = {
	x = cpml.vec3.new(1, 0, 0),
	y = cpml.vec3.new(0, 1, 0),
	z = cpml.vec3.new(0, 0, 1),
}

	camera = cpml.mat4.from_perspective(60, 4/3, 0.0625, 1024.0625)
	premultz = cpml.mat4.from_angle_axis(math.pi/2, axes.z)
	premultx = cpml.mat4.from_angle_axis(math.pi/2, axes.x)
	cpml.mat4.mul(camera, camera, premultz)
	cpml.mat4.mul(camera, camera, premultx)

Code: Select all

[ +0.000, +0.000, +1.299, +0.000, -1.732, +0.000, +0.000, +0.000, +0.000, +1.000, -0.000, -1.000, +0.000, +0.125, -0.000, +0.000 ]
While your matrix that works is:

Code: Select all

{
     1.3,    0,  0,  0,
       0,    0,  1,  1,
       0, -1.7,  0,  0,
    -450, -120, 50, 50,
  }
Here is a .love file (11.0) that switches every second between the "camera" matrix (showing nothing) and your matrix.
Attachments
3DLOVE.love
(598.75 KiB) Downloaded 255 times
PM me on here or elsewhere if you'd like to discuss porting your game to Nintendo Switch via mazette!
personal page and a raycaster
Post Reply

Who is online

Users browsing this forum: No registered users and 61 guests