Need help understanding Raycast's Normal

Hi! Quick question, how can I match a part’s orientation to a ray’s normal?
I tried checking the DevForum and Wiki, and if I understood correctly, a Raycast Normal vector is basically the perpendicular vector of the ray.
What I’m trying to do is basically spawn parts whose orientation match the terrain’s slope. Example:

I managed to make the parts spawns on the appropriate Y level, but unfortunately I can’t seem to match their orientation to the slope, as you can see in this screenshot:

This is the script:

local function placeSpeedArea(pos,player)	
	
	local params = RaycastParams.new()
	params.FilterType = Enum.RaycastFilterType.Blacklist
	params.IgnoreWater = false
	params.FilterDescendantsInstances = {player.Character,workspace.speed, workspace:WaitForChild("IceShards")}
	

	local origin = Vector3.new(pos.X, pos.Y, pos.Z)
	local direction = Vector3.new(0, -1, 0)*100
	local ray = Ray.new(origin, direction)
	local result = workspace:Raycast(origin,direction,params)
	
	
	if result and not speedDB then
		speedDB = true
		local SpeedPad = IcePadModel:Clone()
		SpeedPad.Position = Vector3.new(pos.X, result.Position.Y, pos.Z)
		--SpeedPad.CFrame = CFrame.new(result.Position, result.Normal)
		SpeedPad.Parent = workspace.speed
	end
end

I tried using the method mentioned here

SpeedPad.CFrame = CFrame.new(result.Position,  result.Position + result.Normal)

But unfortunately all it does is this:


I’m not very good at math nor Raycasting so if someone can explain what I’m doing wrong that would be great. I also tried multiplying the CFrame by 90 and 180, but no difference.

4 Likes

I don’t know if this has anything to do with your issue, but as I sat here thinking on this, I thought… firing a ray, against terrain, you might get all sorts of strange normals, considering terrain is ‘lumpy’
So maybe you need to fire 3 rays in a triangular shape, and get your normal based on the 3 hits positions.
I think this would give you better results for the lumpyness, but I have no idea what the math would be to get a normal from 3 points in space, but I’m pretty sure its a common thing to calculate.

Oh, and another quick thought… I think there is an option to select if rays cast on terrain, will have terrain treated as cubes, you want to make sure you do not use that option.

2 Likes

Thank you for your answer! I was really starting to get desperate.
To answer your question: I don’t need it to be absolutely perfect to be honest + I think the lumpiness isn’t a real problem since, if I’m not wrong, the normal returned would be the terrain voxel’s normal and not of the actual geometry.
Anyway, just like you I have no clue on what the math would be, I’m quite bad at that and I’m seriously struggling trying to apply all the methods I can find on the DevForum.

1 Like

Replace this line with

SpeedPad.CFrame = CFrame.lookAt(result.Position, result.Normal) * CFrame.Angles(0, 0, math.rad(90))

Because the front of a cylinder isn’t on one of the flat parts, you need to rotate the CFrame.

11 Likes

You should try generating a sphere out of terrain to see if it will work on the sphere and if it is not just the terrain being lumpy and maybe see if it uses the voxel in raycasting.

Thank you, that’s exactly what I needed. I didn’t realize that’s how cylinders are lol.
By the way, wrong axys, it’s the Y one, not the Z. I fixed it and it works! :slight_smile:

SpeedPad.CFrame = CFrame.lookAt(result.Position, result.Normal) * CFrame.Angles(0, math.rad(90), 0)
4 Likes