How to calculate pixel colors?

I’m trying to make textures work in my recent raycaster. i have access to each pixel’s ray hit position as well as each vertex position of the triangle with their UV coordinates. i honestly have no idea how to fix it :confused:

1 Like

Could you provide more info, like some code please

--!optimize 2
--!native

local event = script.Event
local gui = game.Players.LocalPlayer.PlayerGui.ScreenGui
local VERTS = {
	Vector3.new(0,0,0),
	Vector3.new(10,0,10),
	Vector3.new(0,0,10),
	Vector3.new(10,0,0),
}

local UVs = {
	Vector2.new(0,0),
	Vector2.new(1,1),
	Vector2.new(0,1),
	Vector2.new(1,0),
}

local img = {
	{Color3.new(1, 1, 1),Color3.new(1, 1, 0),Color3.new(0.333333, 1, 1),Color3.new(0.333333, 0.333333, 0)},
	{Color3.new(0.333333, 1, 0),Color3.new(0.333333, 0.666667, 1),Color3.new(1, 0, 0),Color3.new(0, 0, 0.498039)},
	{Color3.new(1, 0.333333, 1),Color3.new(0.333333, 0.333333, 1),Color3.new(0.333333, 1, 1),Color3.new(0.333333, 0, 0.498039)},
	{Color3.new(0, 1, 0),Color3.new(1, 1, 0),Color3.new(0.333333, 1, 1),Color3.new(0.333333, 0.333333, 0)},
}

local ycount = #img-1
local xcount = #img[1]-1

local light = Vector3.new(24, 24.5, -16)
local tris = {
	{1,3,2},
	{1,2,4},
}

local sz = Vector2.new(80,50)

function lerpTime(a,b,c)
	return (c - a) * (b - a) or 0
end

function lerp(a,b,t)
	return a + (b-a)*t
end

local function RayTri(origin, direction, Tri)
	local vertice1, vertice2, vertice3 = VERTS[Tri[1]], VERTS[Tri[2]], VERTS[Tri[3]]
	local uv1, uv2, uv3 = UVs[Tri[1]], UVs[Tri[2]], UVs[Tri[3]]
	--local triobg = {
	--	VERTS[Tri[1]],
	--	VERTS[Tri[2]],
	--	VERTS[Tri[3]],
	--}
	
	--local hi = {}
		
	local edgeAB = vertice2 - vertice1
	local edgeAC = vertice3 - vertice1
	local ao = origin - vertice1
	
	local dao = ao:Cross(direction)
	local normal = edgeAB:Cross(edgeAC)
	local det = -direction:Dot(normal)
	local invDet = 1 / det
	local dst = ao:Dot(normal) * invDet
	local u = edgeAC:Dot(dao) * invDet
	local v = -edgeAB:Dot(dao) * invDet
	local w = 1 - u - v

	--hi.didHit = det >= 1e-6 and dst >= 0 and u >= 0 and v >= 0 and w >= 0
	--hi.point = origin+direction*dst
	--hi.normal = (vertice1.Unit * w + vertice2 * u + vertice3 * v)
	--hi.dst = dst
	
	--local li = (normal:Dot(light)+1)/2
	--local indexed = Tri[4]
	
	--hi.r = indexed.R * li
	--hi.g = indexed.G * li
	--hi.b = indexed.B * li
	--return hi
	
	local li = (normal:Dot(light)+1)/2
	--local indexed = Tri[4]
	local pos = origin+direction*dst
	--local r = indexed.R * li
	--local g = indexed.G * li
	--local b = indexed.B * li
	
	--return det >= 1e-6 and dst >= 0 and u >= 0 and v >= 0 and w >= 0, r, g, b, dst
	local sc = workspace.CurrentCamera.ViewportSize
	local minY,maxY = 0,0
	local minX,maxX = 0,0
	local c = Vector2.zero
	
	c = workspace.CurrentCamera:WorldToViewportPoint(pos) 
	local v1 = workspace.CurrentCamera:WorldToViewportPoint(vertice1) 
	local v2 = workspace.CurrentCamera:WorldToViewportPoint(vertice2)
	local v3 = workspace.CurrentCamera:WorldToViewportPoint(vertice3)
	local hit = det >= 1e-6 and dst >= 0 and u >= 0 and v >= 0 and w >= 0
	minY,maxY = math.min(v1.Y,v2.Y,v3.Y),math.max(v1.Y,v2.Y,v3.Y)
	minX,maxX = math.min(v1.X,v2.X,v3.X),math.max(v1.X,v2.X,v3.X)
	
	local miuvX,mauvX = math.min(uv1.X,uv2.X,uv3.X), math.max(uv1.X,uv2.X,uv3.X)
	local miuvY,mauvY = math.min(uv1.Y,uv2.Y,uv3.Y), math.max(uv1.Y,uv2.Y,uv3.Y)
	
	local cordX = hit and lerp(miuvX,mauvX,(c.X-minX)/(maxX-minX))*4+1 or 0
	local cordY = hit and lerp(miuvY,mauvY,(c.Y-minY)/(maxY-minY))*4+1 or 0
	return hit, (cordX-1)/4, (cordY-1)/4, 0 * li, dst, pos
end

local function rayCollision(ray)
	local mindidHit = false
	local mindst = 9999999999999

	local minr
	local ming
	local minb
	local minpos

	local origin, direction = ray.Origin, ray.Direction

	for _, v in tris do
		local hit, r,g,b, dst, pos = RayTri(origin, direction, v)
		if hit and mindst > dst then
			mindidHit = hit
			minr, ming, minb = r, g, b
			minpos = pos
		end
	end
	
	return mindidHit, minr, ming, minb, minpos
end

local camera = workspace.CurrentCamera
local func = camera.ViewportPointToRay

local function vptr(...)
	return func(camera, ...)
end

local cache = {}

return function(script : Script)
	local actor = script.Parent :: Actor
	
	local si, ei = 0, 0
	
	actor:BindToMessage("cache", function(newCache, newsi, newei)
		cache = newCache
		si = newsi
		ei = newei
	end)

	local function render(clock)
		for i = si,ei,1 do
			local pixelIndex = (i - si) * 4

			local x = buffer.readu16(cache, pixelIndex)
			local y = buffer.readu16(cache, pixelIndex + (2))

			local hit, r,g,b, pos = rayCollision(vptr(x,y,0))

			if hit then
				event:Fire(pixelIndex/4 + si,r,g,b, clock)
			else
				event:Fire(pixelIndex/4 +si, nil, nil, nil, clock)
			end
		end
	end

	actor:BindToMessage("render", render)
	actor:BindToMessageParallel("render_parallel", render)
end

this code runs in the actors that calculate the pixel color. the actual calculation happens in the RayTri function

Interesting you’re trying to make a raycaster in lua…
I have no answer besides looking up how to make your own rendering program (C and C++ have great tutorials on it). I will recommend though to simplify it more, though. If you’re trying to render a triangle with a gradient, but you can’t even render a filled-in gradient, have 3 lines instead that make up the outline of the triangle. It’s easier, since you can just make each line have a gradient to eachother. Then from there, you can try to mathematically fill in the area for the triangle.

If you need inspiration, SDL and OpenGL are rendering libraries. Since they deal with everything about rendering, they have an incredible amount of stuff, so it’d probably be hard to find how to render a triangle, but poking around in there can help you learn about more stuff