读取屏幕点的像素颜色(高效)

see*_*eek 2 c# c++ android unity-game-engine

我需要在屏幕点读取 C# Unity3D 中的像素颜色。

我正在使用渲染纹理和 ReadPixels 方法。每 0.5f 秒使用一次对性能不利(即使我的渲染纹理大小为 128x128px),我正在尝试找到另一种方法来更快地获取此数据。我在某处看到可以直接使用,glReadPixels但我不知道如何使用它?

我需要检测这个地方(屏幕上的点)是否有东西。

这是我现在拥有的代码。

using UnityEngine;
using System;
using System.Collections;

public class ColController : MonoBehaviour
{
    public Camera collCamera;
    public Texture2D tex2d;


    void Start()
    {
        collCamera = GetComponent<Camera>();
        tex2d = new Texture2D(collCamera.targetTexture.width, collCamera.targetTexture.height, TextureFormat.ARGB32, false);

        RenderTexture.active = collCamera.targetTexture;
        StartCoroutine (Execute ());

    }

    IEnumerator Execute()
    {
        while (true) {

                yield return new WaitForEndOfFrame ();
                tex2d = GetRTPixels (collCamera.targetTexture);
                yield return new WaitForSeconds (0.5f);

        }
    }


    static public Texture2D GetRTPixels(RenderTexture rt)
    {
        RenderTexture currentActiveRT = RenderTexture.active;

        RenderTexture.active = rt;

        // Create a new Texture2D and read the RenderTexture image into it
        Texture2D tex = new Texture2D(rt.width, rt.height);
        tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);

        RenderTexture.active = currentActiveRT;
        return tex;
    }

    void Update() {
        screenPos = collCamera.WorldToScreenPoint (player.transform.position);
        positionColor = tex2d.GetPixel ((int)screenPos.x, (int)screenPos.y);

    }


}
Run Code Online (Sandbox Code Playgroud)

Pro*_*mer 6

你必须使用glReadPixels. 过去,只需从函数中的 C# 调用它即可更轻松地实现OnPostRender,但您不能再这样做了。您必须使用GL.IssuePluginEvent调用将截取屏幕截图的函数。

\n\n

您还需要位于 的Unity C++ API 标头(IUnityInterface.hIUnityGraphics.h<UnityInstallationDirecory>\\Editor\\Data\\PluginAPI ) 。

\n\n

我创建了一个名为UnityPluginHeaders 的文件夹,并将IUnityInterface.hIUnityGraphics.h头文件放入其中,以便可以使用#include "UnityPluginHeaders/IUnityInterface.h"和 导入它们#include "UnityPluginHeaders/IUnityGraphics.h"

\n\n

C++ ( ScreenPointPixel.h):

\n\n
#ifndef ANDROIDSCREENSHOT_NATIVE_LIB_H\n#define ANDROIDSCREENSHOT_NATIVE_LIB_H\n\n#define DLLExport __declspec(dllexport)\n\nextern "C"\n{\n#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(WINAPI_FAMILY)\nDLLExport void initScreenPointPixel(void* buffer, int x, int y, int width, int height);\nDLLExport void updateScreenPointPixelBufferPointer(void* buffer);\nDLLExport void updateScreenPointPixelCoordinate(int x, int y);\nDLLExport void updateScreenPointPixelSize(int width, int height);\nint GetScreenPixels(void* buffer, int x, int y, int width, int height);\n\n#else\nvoid initScreenPointPixel(void *buffer, int x, int y, int width, int height);\nvoid updateScreenPointPixelBufferPointer(void *buffer);\nvoid updateScreenPointPixelCoordinate(int x, int y);\nvoid updateScreenPointPixelSize(int width, int height);\n\nint GetScreenPixels(void *buffer, int x, int y, int width, int height);\n#endif\n}\n#endif //ANDROIDSCREENSHOT_NATIVE_LIB_H\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

C++ ( ScreenPointPixel.cpp):

\n\n
#include "ScreenPointPixel.h"\n\n#include <string>\n#include <stdlib.h>\n\n//For Debugging\n//#include "DebugCPP.h"\n//http://stackoverflow.com/questions/43732825/use-debug-log-from-c/43735531#43735531\n\n//Unity Headers\n#include "UnityPluginHeaders/IUnityInterface.h"\n#include "UnityPluginHeaders/IUnityGraphics.h"\n\n\n//Headers for Windows\n#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(WINAPI_FAMILY)\n#include <windows.h>\n#include <gl/GL.h>\n#include <gl/GLU.h>\n#include <stdlib.h>\n#include "glext.h"\n#pragma comment(lib, "opengl32.lib")\n\n//--------------------------------------------------\n\n//Headers for Android\n#elif defined(ANDROID) || defined(__ANDROID__)\n#include <jni.h>\n#include <GLES2/gl2.h>\n#include <GLES2/gl2ext.h>\n//Link lGLESv2 in the CMakeList.txt file\n//LOCAL_LDLIBS += \xe2\x88\x92lGLESv2\n\n//--------------------------------------------------\n\n//Headers for MAC and iOS\n//http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system\n#elif defined(__APPLE__) && defined(__MACH__)\n//Apple OSX and iOS (Darwin)\n#include <TargetConditionals.h>\n#if TARGET_IPHONE_SIMULATOR == 1\n//iOS in Xcode simulator\n#include <OpenGLES/ES2/gl.h>\n#include <OpenGLES/ES2/glext.h>\n#elif TARGET_OS_IPHONE == 1\n//iOS on iPhone, iPad, etc.\n#include <OpenGLES/ES2/gl.h>\n#include <OpenGLES/ES2/glext.h>\n#elif TARGET_OS_MAC == 1\n#include <OpenGL/gl.h>\n#include <OpenGL/glu.h>\n#include <GLUT/glut.h>\n#endif\n\n//--------------------------------------------------\n\n//Headers for Linux\n#elif defined(__linux__)\n#include <GL/gl.h>\n#include <GL/glu.h>\n#endif\n\nstatic void* screenPointPixelData = nullptr;\nstatic int _x;\nstatic int _y;\nstatic int _width;\nstatic int _height;\n\n//----------------------------Enable Screenshot-----------------------------\nvoid initScreenPointPixel(void* buffer, int x, int y, int width, int height) {\n    screenPointPixelData = buffer;\n    _x = x;\n    _y = y;\n    _width = width;\n    _height = height;\n}\n\nvoid updateScreenPointPixelBufferPointer(void* buffer) {\n    screenPointPixelData = buffer;\n}\n\nvoid updateScreenPointPixelCoordinate(int x, int y) {\n    _x = x;\n    _y = y;\n}\n\nvoid updateScreenPointPixelSize(int width, int height) {\n    _width = width;\n    _height = height;\n}\n\nint GetScreenPixels(void* buffer, int x, int y, int width, int height) {\n    if (glGetError())\n        return -1;\n\n    //glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);\n    glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);\n\n    if (glGetError())\n        return -2;\n    return 0;\n}\n\n//----------------------------UNITY RENDERING CALLBACK-----------------------------\n\n// Plugin function to handle a specific rendering event\nstatic void UNITY_INTERFACE_API OnRenderEventScreenPointPixel(int eventID)\n{\n    //Put rendering code below\n    if (screenPointPixelData == nullptr) {\n        //Debug::Log("Pointer is null", Color::Red);\n        return;\n    }\n    int result = GetScreenPixels(screenPointPixelData, _x, _y, _width, _height);\n\n    //std::string log_msg = "Cobol " + std::to_string(result);\n    //Debug::Log(log_msg, Color::Green);\n}\n\n// Freely defined function to pass a callback to plugin-specific scripts\nextern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API\nGetRenderEventScreenPointPixelFunc()\n{\n    return OnRenderEventScreenPointPixel;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

从 Android Studio 编译/构建时,它应该为您提供两个文件夹(目录中的armeabi-v7ax86<ProjectDirectory>\\app\\build\\intermediates\\cmake\\release\\obj。它们都应该包含共享的 *.so 库。如果您无法为Android Studio编译此库,请使用以下副本我为此制作了 Android Studio 项目,您可以使用它来生成共享的 *.so 库。

\n\n

将这两个文件夹放置在 Unity 项目文件夹中Assets\\Plugins\\Android\\libs

\n\n

您现在应该拥有:

\n\n

Assets\\Plugins\\Android\\libs\\armeabi-v7a\\libScreenPointPixel-lib.so

\n\n

\n\n

Assets\\Plugins\\Android\\libs\\x86\\libScreenPointPixel-lib.so

\n\n
\n\n

C#测试代码:

\n\n

创建一个简单的RawImage小组件并将其放置在屏幕的右上角。将其拖到RawImage下面脚本中的 rawImageColor 插槽中。当您单击屏幕上的任意位置时,该屏幕点的像素颜色应显示在该屏幕上rawImageColorRawImage 上。

\n\n

C#

\n\n
using System;\nusing System.Collections;\nusing System.Runtime.InteropServices;\nusing UnityEngine;\nusing UnityEngine.UI;\n\npublic class ScreenPointPixel : MonoBehaviour\n{\n    [DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.Cdecl)]\n    public static extern void initScreenPointPixel(IntPtr buffer, int x, int y, int width, int height);\n    //-------------------------------------------------------------------------------------\n    [DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.Cdecl)]\n    public static extern void updateScreenPointPixelBufferPointer(IntPtr buffer);\n    //-------------------------------------------------------------------------------------\n    [DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.Cdecl)]\n    public static extern void updateScreenPointPixelCoordinate(int x, int y);\n    //-------------------------------------------------------------------------------------\n    [DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.Cdecl)]\n    public static extern void updateScreenPointPixelSize(int width, int height);\n    //-------------------------------------------------------------------------------------\n\n    //-------------------------------------------------------------------------------------\n    [DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.StdCall)]\n    private static extern IntPtr GetRenderEventScreenPointPixelFunc();\n    //-------------------------------------------------------------------------------------\n    int width = 500;\n    int height = 500;\n\n    //Where Pixel data will be saved\n    byte[] screenData;\n    //Where handle that pins the Pixel data will stay\n    GCHandle pinHandler;\n\n    //Used to test the color\n    public RawImage rawImageColor;\n\n    // Use this for initialization\n    void Awake()\n    {\n        Resolution res = Screen.currentResolution;\n        width = res.width;\n        height = res.height;\n\n        //Allocate array to be used\n        screenData = new byte[width * height * 4];\n\n        //Pin the Array so that it doesn\'t move around\n        pinHandler = GCHandle.Alloc(screenData, GCHandleType.Pinned);\n\n        //Register the screenshot and pass the array that will receive the pixels\n        IntPtr arrayPtr = pinHandler.AddrOfPinnedObject();\n\n        initScreenPointPixel(arrayPtr, 0, 0, width, height);\n\n        StartCoroutine(caller());\n    }\n\n    IEnumerator caller()\n    {\n        while (true)\n        {\n            //Use mouse position as the pixel position\n            //Input.tou\n\n\n#if UNITY_ANDROID || UNITY_IOS || UNITY_WSA_10_0\n            if (!(Input.touchCount > 0))\n            {\n                yield return null;\n                continue;\n            }\n\n            //Use touch position as the pixel position\n            int x = Mathf.FloorToInt(Input.GetTouch(0).position.x);\n            int y = Mathf.FloorToInt(Input.GetTouch(0).position.y);\n#else\n            //Use mouse position as the pixel position\n            int x = Mathf.FloorToInt(Input.mousePosition.x);\n            int y = Mathf.FloorToInt(Input.mousePosition.y);\n#endif\n            //Change this to any location from the screen you want\n            updateScreenPointPixelCoordinate(x, y);\n\n            //Must be 1 and 1\n            updateScreenPointPixelSize(1, 1);\n\n            //Take screenshot of the screen\n            GL.IssuePluginEvent(GetRenderEventScreenPointPixelFunc(), 1);\n\n            //Get the Color\n            Color32 tempColor = new Color32();\n            tempColor.r = screenData[0];\n            tempColor.g = screenData[1];\n            tempColor.b = screenData[2];\n            tempColor.a = screenData[3];\n\n            //Test it by assigning it to a raw image\n            rawImageColor.color = tempColor;\n\n            //Wait for a frame\n            yield return null;\n        }\n    }\n\n    void OnDisable()\n    {\n        //Unpin the array when disabled\n        pinHandler.Free();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n