Jump to content

2D Drawing to Texture


Josh

2,195 views

 Share

I have been working on 2D rendering off and on since October. Why am I putting so much effort into something that was fairly simple in Leadwerks 4? I have been designing a system in anticipation of some features I want to see in the GUI, namely VR support and in-game 3D user interfaces. These are both accomplished with 2D drawing performed on a texture. Our system of sprite layers, cameras, and sprites was necessary in order to provide enough control to accomplish this.

I now have 2D drawing to a texture working, this time as an official supported feature. In Leadwerks 4, some draw-to-texture features were supported, but it was through undocumented commands due to the complex design of shared resources between OpenGL contexts. Vulkan does not have this problem because everything, including contexts (or rather, the VK equivalent) is bound to an abstract VkInstance object.

Untitled.png.ef2a6cc34887361aad6249eaa97f6cf3.png

Here is the Lua code that makes this program:

--Get the primary display
local displaylist = ListDisplays()
local display = displaylist[1];
if display == nil then DebugError("Primary display not found.") end
local displayscale = display:GetScale()

--Create a window
local window = CreateWindow(display, "2D Drawing to Texture", 0, 0, math.min(1280 * displayscale.x, display.size.x), math.min(720 * displayscale.y, display.size.y), WINDOW_TITLEBAR)

--Create a rendering framebuffer
local framebuffer = CreateFramebuffer(window);

--Create a world
local world = CreateWorld()

--Create second camera
local texcam = CreateCamera(world)

--Create a camera
local camera = CreateCamera(world)
camera:Turn(45,-45,0)
camera:Move(0,0,-2)
camera:SetClearColor(0,0,1,1)

--Create a texture buffer
local texbuffer = CreateTextureBuffer(512,512,1,true)
texcam:SetRenderTarget(texbuffer)

--Create scene
local box = CreateBox(world)

--Create render-to-texture material
local material = CreateMaterial()
local tex = texbuffer:GetColorBuffer()
material:SetTexture(tex, TEXTURE_BASE)
box:SetMaterial(material)

--Create a light
local light = CreateLight(world,LIGHT_DIRECTIONAL)
light:SetRotation(55,-55,0)
light:SetColor(2,2,2,1)

--Create a sprite layer. This can be shared across different cameras for control over which cameras display the 2D elements
local layer = CreateSpriteLayer(world)
texcam:AddSpriteLayer(layer)
texcam:SetPosition(0,1000,0)--put the camera really far away

--Load a sprite to display
local sprite = LoadSprite(layer, "Materials/Sprites/23.svg", 0, 0.5)
sprite:MidHandle(true,true)
sprite:SetPosition(texbuffer.size.x * 0.5, texbuffer.size.y * 0.5)

--Load font
local font = LoadFont("Fonts/arial.ttf", 0)

--Text shadow
local textshadow = CreateText(layer, font, "Hello!", 36 * displayscale.y, TEXT_LEFT, 1)
textshadow:SetColor(0,0,0,1)
textshadow:SetPosition(50,30)
textshadow:SetRotation(90)

--Create text
text = textshadow:Instantiate(layer)
text:SetColor(1,1,1,1)
text:SetPosition(52,32)
text:SetRotation(90)

--Main loop
while window:Closed() == false do

	sprite:SetRotation(CurrentTime() / 30)

	world:Update()
	world:Render(framebuffer)

end

I have also added a GetTexCoords() command to the PickInfo structure. This will calculate the tangent and bitangent for the picked triangle and then calculate the UV coordinate at the picked position. It is necessary to calculate the non-normalized tangent and bitangent to get the texture coordinate, because the values that are stored in the vertex array are normalized and do not include the length of the vectors.

local pick = camera:Pick(framebuffer, mousepos.x, mousepos.y, 0, true, 0)
if pick ~= nil then
	local texcoords = pick:GetTexCoords()
	Print(texcoords)
end

Maybe I will make this into a Mesh method like GetPolygonTexCoord(), which would work just as well but could potentially be useful for other things. I have not decided yet.

Now that we have 2D drawing to a texture, and the ability to calculate texture coordinates at a position on a mesh, the next step will be to set up a GUI displayed on a 3D surface, and to send input events to the GUI based on the user interactions in 3D space. The texture could be applied to a computer panel, like many of the interfaces in the newer DOOM games, or it could be used as a panel floating in the air that can be interacted with VR controllers.

  • Like 1
 Share

4 Comments


Recommended Comments

I suppose, texcam will be rendered every frame, just like the normal camera. Would there be an option to render the camera only on demand? Like some sort of photograph? Or with a lower frequency for performance optimization?

Link to comment
21 minutes ago, Ma-Shell said:

I suppose, texcam will be rendered every frame, just like the normal camera. Would there be an option to render the camera only on demand? Like some sort of photograph? Or with a lower frequency for performance optimization?

Hide the camera and it won’t render. There isn’t strict synchronization of the rendering and main threads so I am not sure how you would make sure it just rendered one single frame. Something to think about.

Link to comment
On 1/12/2020 at 5:17 PM, Ma-Shell said:

I suppose, texcam will be rendered every frame, just like the normal camera. Would there be an option to render the camera only on demand? Like some sort of photograph? Or with a lower frequency for performance optimization?

Here is what I came up with:

void Camera::SetRealTime(const bool realtimemode)
void Camera::Refresh()

Refresh will cause a non-realtime camera to render once before it is disabled automatically, until the next refresh.

  • Like 1
Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...