Here’s a more generalized solution. I used brute force
local Debris = game:GetService("Debris")
local shieldEffectPart = script.Parent
local partSize = shieldEffectPart.Size
local CANVAS_SIZE_PER_STUD = 1
local surfaceGuis: {SurfaceGui} = {
shieldEffectPart.FrontGui,
shieldEffectPart.BackGui,
shieldEffectPart.LeftGui,
shieldEffectPart.RightGui,
shieldEffectPart.TopGui,
shieldEffectPart.BottomGui
}
local function GetWorldOrientedSurface(part: BasePart, normalId: Enum.NormalId): (CFrame, Vector2)
local cf = part.CFrame
local rot = cf - cf.Position
local nObject = Vector3.fromNormalId(normalId)
local nWorld = rot * nObject
-- get orthogonal vector by utilizing the order of NormalId enums
-- i.e. Front.Value is 5 -> (5+1)%6 = 0 -> Right.Value
local xWorld = rot * Vector3.fromNormalId((normalId.Value + 1) % 6)
-- get other orthogonal vector
local zWorld = nWorld:Cross(xWorld)
-- make them both point "generally down"
if xWorld.Y > 0 then xWorld = -xWorld end
if zWorld.Y > 0 then zWorld = -zWorld end
-- choose the one pointing "more down" one as the z axis for the surface
if xWorld.Y < zWorld.Y then zWorld = xWorld end
-- redefine x axis based on that
xWorld = nWorld:Cross(zWorld)
local surfaceRot = CFrame.fromMatrix(Vector3.new(), xWorld, nWorld, zWorld)
-- get width of part in direction of x and y
local sizeInWorldSpace = rot * part.Size
local sizeInSurfaceSpace = surfaceRot:Inverse() * sizeInWorldSpace
-- get position on surface
local surfaceCFrame = surfaceRot + cf.Position + nWorld * math.abs(sizeInSurfaceSpace.Y) / 2
return surfaceCFrame, Vector2.new(math.abs(sizeInSurfaceSpace.X), math.abs(sizeInSurfaceSpace.Z))
end
function CalculatePixelWorldPosition(face: Enum.NormalId, pixelPosition: Vector2, editableImage: EditableImage): Vector3
local _, surfaceSize = GetWorldOrientedSurface(shieldEffectPart, face)
local pixelObjectPosition: Vector3 = Vector3.new()
local adjustedPixelPosition = pixelPosition / CANVAS_SIZE_PER_STUD
if face == Enum.NormalId.Front then
pixelObjectPosition = Vector3.new(-adjustedPixelPosition.X + surfaceSize.X/2, -adjustedPixelPosition.Y + surfaceSize.Y/2, -shieldEffectPart.Size.Z/2)
elseif face == Enum.NormalId.Back then
pixelObjectPosition = Vector3.new(adjustedPixelPosition.X - surfaceSize.X/2, -adjustedPixelPosition.Y + surfaceSize.Y/2, shieldEffectPart.Size.Z/2)
elseif face == Enum.NormalId.Right then
pixelObjectPosition = Vector3.new(shieldEffectPart.Size.X/2, -adjustedPixelPosition.Y + surfaceSize.Y/2, -adjustedPixelPosition.X + surfaceSize.X/2)
elseif face == Enum.NormalId.Left then
pixelObjectPosition = Vector3.new(-shieldEffectPart.Size.X/2, -adjustedPixelPosition.Y + surfaceSize.Y/2, adjustedPixelPosition.X - surfaceSize.X/2)
elseif face == Enum.NormalId.Top then
pixelObjectPosition = Vector3.new(adjustedPixelPosition.Y - surfaceSize.Y/2, shieldEffectPart.Size.Y/2, -adjustedPixelPosition.X + surfaceSize.X/2)
elseif face == Enum.NormalId.Bottom then
pixelObjectPosition = Vector3.new(-adjustedPixelPosition.Y + surfaceSize.Y/2, -shieldEffectPart.Size.Y/2, -adjustedPixelPosition.X + surfaceSize.X/2)
end
return shieldEffectPart.CFrame:PointToWorldSpace(pixelObjectPosition)
end
for _, surfaceGui in surfaceGuis do
local _, surfaceSize = GetWorldOrientedSurface(shieldEffectPart, surfaceGui.Face)
local editableImage = Instance.new("EditableImage")
editableImage.Parent = surfaceGui.ImageLabel
editableImage:Resize(surfaceSize * Vector2.new(CANVAS_SIZE_PER_STUD, CANVAS_SIZE_PER_STUD))
end
local partCloned = false
while true do
task.wait()
for _, surfaceGui in surfaceGuis do
local editableImage: EditableImage = surfaceGui.ImageLabel.EditableImage
local pixels: {number} = {}
for y = 1, editableImage.Size.Y do
for x = 1, editableImage.Size.X do
local pixelWorldPosition = CalculatePixelWorldPosition(surfaceGui.Face, Vector2.new(x, y), editableImage)
local tickModulus = tick() % 2048
local perlinNoiseWorldPos = pixelWorldPosition/5+Vector3.new(tickModulus, tickModulus, tickModulus)
local pixelFactor = (math.noise(perlinNoiseWorldPos.X, perlinNoiseWorldPos.Y, perlinNoiseWorldPos.Z)+1)/2
local alpha = math.sin((pixelWorldPosition.X + pixelWorldPosition.Y + pixelWorldPosition.Z) + tick())
--table.insert(pixels, x/editableImage.Size.X)
--table.insert(pixels, y/editableImage.Size.Y)
--table.insert(pixels, 0)
--table.insert(pixels, 1)
table.insert(pixels, 1)
table.insert(pixels, 1)
table.insert(pixels, 1)
table.insert(pixels, alpha)
--Uncomment to see pixelWorldPosition
--if not partCloned then
-- local part = game.Workspace.clonepart:Clone()
-- part.Anchored = true
-- part.Size = Vector3.new(0.1, 0.1, 0.1)
-- part.Parent = workspace
-- part.Position = pixelWorldPosition
-- part.Color = Color3.new(x/editableImage.Size.X, y/editableImage.Size.Y, 0)
--end
end
end
--print(pixels)
--print(#pixels, editableImage.Size.X * editableImage.Size.Y * 4)
editableImage:WritePixels(Vector2.new(0,0), editableImage.Size, pixels)
end
partCloned = true
end