我需要在屏幕上读取C#Unity3D中的像素颜色。
我正在使用Render Texture和ReadPixels方法。
每0.5秒使用它对性能有害(即使我的渲染纹理大小为128x128px),我正试图找到另一种方法来更快地获得这些数据。
我在某处读到可以直接使用PreferenceDataStore
,但我不知道如何使用它?
我需要检测在这个地方(屏幕上的点)是否是某种东西。
这是我目前的代码。
glReadPixels
答案 0 :(得分:3)
您必须使用glReadPixels
。通过从OnPostRender
函数中的C#调用它可以更容易实现,但是你不能再这样做了。您必须使用GL.IssuePluginEvent
来调用将截取屏幕截图的功能。
您还需要位于<UnityInstallationDirecory>\Editor\Data\PluginAPI
的Unity C ++ API标头( IUnityInterface.h 和 IUnityGraphics.h )。
我创建了一个名为 UnityPluginHeaders 的文件夹,并将 IUnityInterface.h 和 IUnityGraphics.h 头文件放入其中,以便可以导入它们使用#include "UnityPluginHeaders/IUnityInterface.h"
和#include "UnityPluginHeaders/IUnityGraphics.h"
。
C ++ (ScreenPointPixel.h
):
#ifndef ANDROIDSCREENSHOT_NATIVE_LIB_H
#define ANDROIDSCREENSHOT_NATIVE_LIB_H
#define DLLExport __declspec(dllexport)
extern "C"
{
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(WINAPI_FAMILY)
DLLExport void initScreenPointPixel(void* buffer, int x, int y, int width, int height);
DLLExport void updateScreenPointPixelBufferPointer(void* buffer);
DLLExport void updateScreenPointPixelCoordinate(int x, int y);
DLLExport void updateScreenPointPixelSize(int width, int height);
int GetScreenPixels(void* buffer, int x, int y, int width, int height);
#else
void initScreenPointPixel(void *buffer, int x, int y, int width, int height);
void updateScreenPointPixelBufferPointer(void *buffer);
void updateScreenPointPixelCoordinate(int x, int y);
void updateScreenPointPixelSize(int width, int height);
int GetScreenPixels(void *buffer, int x, int y, int width, int height);
#endif
}
#endif //ANDROIDSCREENSHOT_NATIVE_LIB_H
C ++ (ScreenPointPixel.cpp
):
#include "ScreenPointPixel.h"
#include <string>
#include <stdlib.h>
//For Debugging
//#include "DebugCPP.h"
//http://stackoverflow.com/questions/43732825/use-debug-log-from-c/43735531#43735531
//Unity Headers
#include "UnityPluginHeaders/IUnityInterface.h"
#include "UnityPluginHeaders/IUnityGraphics.h"
//Headers for Windows
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(WINAPI_FAMILY)
#include <windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#include <stdlib.h>
#include "glext.h"
#pragma comment(lib, "opengl32.lib")
//--------------------------------------------------
//Headers for Android
#elif defined(ANDROID) || defined(__ANDROID__)
#include <jni.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
//Link lGLESv2 in the CMakeList.txt file
//LOCAL_LDLIBS += −lGLESv2
//--------------------------------------------------
//Headers for MAC and iOS
//http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system
#elif defined(__APPLE__) && defined(__MACH__)
//Apple OSX and iOS (Darwin)
#include <TargetConditionals.h>
#if TARGET_IPHONE_SIMULATOR == 1
//iOS in Xcode simulator
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#elif TARGET_OS_IPHONE == 1
//iOS on iPhone, iPad, etc.
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#elif TARGET_OS_MAC == 1
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#endif
//--------------------------------------------------
//Headers for Linux
#elif defined(__linux__)
#include <GL/gl.h>
#include <GL/glu.h>
#endif
static void* screenPointPixelData = nullptr;
static int _x;
static int _y;
static int _width;
static int _height;
//----------------------------Enable Screenshot-----------------------------
void initScreenPointPixel(void* buffer, int x, int y, int width, int height) {
screenPointPixelData = buffer;
_x = x;
_y = y;
_width = width;
_height = height;
}
void updateScreenPointPixelBufferPointer(void* buffer) {
screenPointPixelData = buffer;
}
void updateScreenPointPixelCoordinate(int x, int y) {
_x = x;
_y = y;
}
void updateScreenPointPixelSize(int width, int height) {
_width = width;
_height = height;
}
int GetScreenPixels(void* buffer, int x, int y, int width, int height) {
if (glGetError())
return -1;
//glReadPixels(x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
if (glGetError())
return -2;
return 0;
}
//----------------------------UNITY RENDERING CALLBACK-----------------------------
// Plugin function to handle a specific rendering event
static void UNITY_INTERFACE_API OnRenderEventScreenPointPixel(int eventID)
{
//Put rendering code below
if (screenPointPixelData == nullptr) {
//Debug::Log("Pointer is null", Color::Red);
return;
}
int result = GetScreenPixels(screenPointPixelData, _x, _y, _width, _height);
//std::string log_msg = "Cobol " + std::to_string(result);
//Debug::Log(log_msg, Color::Green);
}
// Freely defined function to pass a callback to plugin-specific scripts
extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
GetRenderEventScreenPointPixelFunc()
{
return OnRenderEventScreenPointPixel;
}
从Android Studio编译/构建时,它应该为您提供两个文件夹({em> armeabi-v7a 和 x86 在<ProjectDirectory>\app\build\intermediates\cmake\release\obj
目录。它们都应包含共享* .so库。如果您无法为 Android Studio 编译此项,请使用我为此here制作的Android Studio项目副本。您可以使用它来生成共享* .so图书馆。
将两个文件夹放在Assets\Plugins\Android\libs
的Unity项目文件夹中。
你现在应该:
Assets\Plugins\Android\libs\armeabi-v7a\libScreenPointPixel-lib.so
。
和
Assets\Plugins\Android\libs\x86\libScreenPointPixel-lib.so
。
C#测试代码:
创建一个简单的小RawImage
组件,并将其放置在屏幕的右上角中。将RawImage
拖动到下面脚本中的rawImageColor槽。当您单击屏幕上的任意位置时,该屏幕点的像素颜色应显示在rawImageColor
RawImage上。
<强> C#强>:
using System;
using System.Collections;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
public class ScreenPointPixel : MonoBehaviour
{
[DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.Cdecl)]
public static extern void initScreenPointPixel(IntPtr buffer, int x, int y, int width, int height);
//-------------------------------------------------------------------------------------
[DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.Cdecl)]
public static extern void updateScreenPointPixelBufferPointer(IntPtr buffer);
//-------------------------------------------------------------------------------------
[DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.Cdecl)]
public static extern void updateScreenPointPixelCoordinate(int x, int y);
//-------------------------------------------------------------------------------------
[DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.Cdecl)]
public static extern void updateScreenPointPixelSize(int width, int height);
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
[DllImport("ScreenPointPixel-lib", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetRenderEventScreenPointPixelFunc();
//-------------------------------------------------------------------------------------
int width = 500;
int height = 500;
//Where Pixel data will be saved
byte[] screenData;
//Where handle that pins the Pixel data will stay
GCHandle pinHandler;
//Used to test the color
public RawImage rawImageColor;
// Use this for initialization
void Awake()
{
Resolution res = Screen.currentResolution;
width = res.width;
height = res.height;
//Allocate array to be used
screenData = new byte[width * height * 4];
//Pin the Array so that it doesn't move around
pinHandler = GCHandle.Alloc(screenData, GCHandleType.Pinned);
//Register the screenshot and pass the array that will receive the pixels
IntPtr arrayPtr = pinHandler.AddrOfPinnedObject();
initScreenPointPixel(arrayPtr, 0, 0, width, height);
StartCoroutine(caller());
}
IEnumerator caller()
{
while (true)
{
//Use mouse position as the pixel position
//Input.tou
#if UNITY_ANDROID || UNITY_IOS || UNITY_WSA_10_0
if (!(Input.touchCount > 0))
{
yield return null;
continue;
}
//Use touch position as the pixel position
int x = Mathf.FloorToInt(Input.GetTouch(0).position.x);
int y = Mathf.FloorToInt(Input.GetTouch(0).position.y);
#else
//Use mouse position as the pixel position
int x = Mathf.FloorToInt(Input.mousePosition.x);
int y = Mathf.FloorToInt(Input.mousePosition.y);
#endif
//Change this to any location from the screen you want
updateScreenPointPixelCoordinate(x, y);
//Must be 1 and 1
updateScreenPointPixelSize(1, 1);
//Take screenshot of the screen
GL.IssuePluginEvent(GetRenderEventScreenPointPixelFunc(), 1);
//Get the Color
Color32 tempColor = new Color32();
tempColor.r = screenData[0];
tempColor.g = screenData[1];
tempColor.b = screenData[2];
tempColor.a = screenData[3];
//Test it by assigning it to a raw image
rawImageColor.color = tempColor;
//Wait for a frame
yield return null;
}
}
void OnDisable()
{
//Unpin the array when disabled
pinHandler.Free();
}
}