/// <summary> /// Creates cookies as Texture2Ds, used when performing keystone (pixel-by-pixel fallback) /// </summary> /// <param name="precalculatedData"></param> /// <param name="precalculatedCookie"></param> /// <param name="imageToProject"></param> public Cookie(ProjectedImageInCookieData precalculatedData, Texture precalculatedCookie, Texture imageToProject) { // this contructor is only used in pixel-by-pixel mode pixelByPixel = true; projectedImage = imageToProject; projectedImage2D = (Texture2D)projectedImage; if (projectedImage2D != null) { imageColours = projectedImage2D.GetPixels32(); } else { imageColours = null; } projectedImageData = precalculatedData; if (projectedImageData.colour) { imageType = ImageType.Colour; } // create cookies redCookie = new Texture2D(precalculatedData.textureSize, precalculatedData.textureSize, TextureFormat.Alpha8, false); redCookie2D = (Texture2D)redCookie; redCookie.wrapMode = TextureWrapMode.Clamp; // copy blacks from precalculated cookie Graphics.CopyTexture(precalculatedCookie, redCookie); redColors = redCookie2D.GetPixels32(); if (imageType == ImageType.Colour) { greenCookie = new Texture2D(precalculatedData.textureSize, precalculatedData.textureSize, TextureFormat.Alpha8, false); blueCookie = new Texture2D(precalculatedData.textureSize, precalculatedData.textureSize, TextureFormat.Alpha8, false); greenCookie2D = (Texture2D)greenCookie; blueCookie2D = (Texture2D)blueCookie; greenCookie.wrapMode = blueCookie.wrapMode = TextureWrapMode.Clamp; Graphics.CopyTexture(precalculatedCookie, greenCookie); Graphics.CopyTexture(precalculatedCookie, blueCookie); greenColors = greenCookie2D.GetPixels32(); blueColors = blueCookie2D.GetPixels32(); } UpdateCookie(false); }
/// <summary> /// Calculates where the image will be in the cookie and creates the whole cookie texture /// </summary> void UpdateCookie(bool doBlacks = true) { if (pixelByPixel) { if (redColors == null) // have we already processed? { return; } if (doBlacks) { float resultWidth, resultHeight; resultWidth = distance / data.ratio; resultHeight = resultWidth / data.aspect; // calculate image position in pixels // calculate shift in meters Vector2 shift = new Vector2(resultWidth * (data.shift_h / 200.0f), resultHeight * (data.shift_v / 200.0f)); // position of image in meters, relative to projector centre float imageLeftMeters = maxImageEdgeDistance - (resultWidth / 2.0f) + shift.x; float imageTopMeters = maxImageEdgeDistance - (resultHeight / 2.0f) + shift.y; // posisiton of image in the cookie texture int imageLeftPixels = (int)(imageLeftMeters * metersToPixels); int imageTopPixels = (int)(imageTopMeters * metersToPixels); // size of image in the cookie texture int imageWidthPixels = (int)(resultWidth * metersToPixels); int imageHeightPixels = (int)(resultHeight * metersToPixels); // keystone width float keystoneH = data.keystone_h / 100f; if (keystoneH < 0) { keystoneH *= -1; } float keystoneMinWidth = imageWidthPixels * (1f - keystoneH); float keystoneV = data.keystone_v / 100f; if (keystoneV < 0) { keystoneV *= -1; } float keystoneMinHeight = imageHeightPixels * (1f - keystoneV); // data about where the image is located in the cookie projectedImageData = new ProjectedImageInCookieData(imageLeftPixels, imageTopPixels, imageWidthPixels, imageHeightPixels, keystoneMinWidth, keystoneMinHeight, data.keystone_h < 0, data.keystone_v < 0, imageType == ImageType.Colour, textureSize); } PixelCalcArgs args; if (projectedImage) { args = new PixelCalcArgs(projectedImage.width, projectedImage.height, imageType, doBlacks, 0, redColors.Length); } else { args = new PixelCalcArgs(0, 0, imageType, doBlacks, 0, redColors.Length); } CalculatePixels(args); // apply new colours once cookies are calculated redCookie2D.SetPixels32(redColors); redCookie2D.Apply(); if (imageType == ImageType.Colour) { greenCookie2D.SetPixels32(greenColors); greenCookie2D.Apply(); blueCookie2D.SetPixels32(blueColors); blueCookie2D.Apply(); } } else // shader-based approach { // safety nets if (projectedImage == null) // should never happen, as we create a white image when null is passed in { Debug.Log("ProjectedImage is null!"); return; } if (projectedImage as Texture2D != null) { if (((Texture2D)projectedImage).format != TextureFormat.RGBA32) { Debug.Log("Texture " + projectedImage.name + " is not correct TextureFormat RGBA32"); return; } } if (projectedImage as RenderTexture != null) { if (((RenderTexture)projectedImage).format != RenderTextureFormat.ARGB32) { Debug.Log("RenderTexture " + projectedImage.name + " is not correct RenderTextureFormat ARGB32"); return; } } // imageWithBorder is a full-size colour copy of the original image, but with a 1px black border to enable the lens shift effect Graphics.CopyTexture(projectedImage, 0, 0, 0, 0, projectedImage.width, projectedImage.height, imageWithBorder, 0, 0, blackBorderSize, blackBorderSize); // apply the shift in the cookie Graphics.Blit(imageWithBorder, imageShifted, cookieSpaceScale, cookieSpaceOffset); // split the shifted image into its 3 channels Graphics.Blit(imageShifted, (RenderTexture)redCookie, stripRed); Graphics.Blit(imageShifted, (RenderTexture)greenCookie, stripGreen); Graphics.Blit(imageShifted, (RenderTexture)blueCookie, stripBlue); } // garbage collection, otherwise RAM usage goes waaaaaaay up #if UNITY_EDITOR if (EditorApplication.isPlaying) { #endif // deallocate memory now that cookie has been created if (projectedImage as Texture2D != null) { projectedImage = null; } imageColours = null; if (!supportLiveUpdate) { redColors = greenColors = blueColors = null; } #if UNITY_EDITOR } #endif }