Shi*_*ing 5 c# c++ pinvoke unity-game-engine
在该项目中,我通过 DllImports 使用 Unity3D 的 C# 脚本和 C++。我的目标是游戏场景有 2 个立方体(Cube & Cube2),其中一个立方体纹理显示通过我的笔记本电脑摄像头和 Unity 的实时视频webCamTexture.Play(),另一个立方体纹理显示外部 C++ 函数处理的视频ProcessImage()。
代码上下文:
在c++中,我定义了
struct Color32
{
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};
Run Code Online (Sandbox Code Playgroud)
函数是
extern "C"
{
Color32* ProcessImage(Color32* raw, int width, int height);
}
...
Color32* ProcessImage(Color32* raw, int width, int height)
{
for(int i=0; i<width*height ;i++)
{
raw[i].r = raw[i].r-2;
raw[i].g = raw[i].g-2;
raw[i].b = raw[i].b-2;
raw[i].a = raw[i].a-2;
}
return raw;
}
Run Code Online (Sandbox Code Playgroud)
C#:
声明和导入
public GameObject cube;
public GameObject cube2;
private Texture2D tx2D;
private WebCamTexture webCamTexture;
[DllImport("test22")] /*the name of Plugin is test22*/
private static extern Color32[] ProcessImage(Color32[] rawImg,
int width, int height);
Run Code Online (Sandbox Code Playgroud)
获取相机情况并设置cube1、cube2纹理
void Start()
{
WebCamDevice[] wcd = WebCamTexture.devices;
if(wcd.Length==0)
{
print("Cannot find a camera");
Application.Quit();
}
else
{
webCamTexture = new WebCamTexture(wcd[0].name);
cube.GetComponent<Renderer>().material.mainTexture = webCamTexture;
tx2D = new Texture2D(webCamTexture.width, webCamTexture.height);
cube2.GetComponent<Renderer>().material.mainTexture = tx2D;
webCamTexture.Play();
}
}
Run Code Online (Sandbox Code Playgroud)
通过将数据发送到外部 C++ 函数DllImports并使用 接收处理后的数据Color32[] a。最后,我使用 UnitySetPixels32设置tx2D(Cube2) 纹理:
void Update()
{
Color32[] rawImg = webCamTexture.GetPixels32();
System.Array.Reverse(rawImg);
Debug.Log("Test1");
Color32[] a = ProcessImage(rawImg, webCamTexture.width, webCamTexture.height);
Debug.Log("Test2");
tx2D.SetPixels32(a);
tx2D.Apply();
}
Run Code Online (Sandbox Code Playgroud)
结果:


结果是立方体 1 的纹理仅显示实时视频,而无法显示使用立方体 2 的纹理处理的数据。
错误:
调用 SetPixels32 时,数组中的像素数量无效 UnityEngine.Texture2D:SetPixels32(Color32[]) Webcam:Update() (位于 Assets/Scripts/Webcam.cs:45)
我不明白为什么当我将数组 a 输入到 SetPixels32 时数组中的像素数无效
有任何想法吗?
更新(2018 年 10 月 10 日)
感谢@Programmer,现在它可以通过引脚内存工作。

顺便说一句,我发现一些关于 Unity 引擎的小问题。当Unity相机运行0到1秒之间时,webCamTexture.width或者webCamTexture.height总是返回16x16尺寸,甚至请求更大的图像,例如1280x720,然后它将在1秒后返回正确的尺寸。(可能是几帧)所以,我参考这篇文章Process()并延迟2秒在函数中运行Update()并在函数中重置Texture2D大小Process()。它会工作得很好:
delaytime = 0;
void Update()
{
delaytime = delaytime + Time.deltaTime;
Debug.Log(webCamTexture.width);
Debug.Log(webCamTexture.height);
if (delaytime >= 2f)
Process();
}
unsafe void Process()
{
...
if ((Test.width != webCamTexture.width) || Test.height != webCamTexture.height)
{
Test = new Texture2D(webCamTexture.width, webCamTexture.height, TextureFormat.ARGB32, false, false);
cube2.GetComponent<Renderer>().material.mainTexture = Test;
Debug.Log("Fixed Texture dimension");
}
...
}
Run Code Online (Sandbox Code Playgroud)

C# 不返回理解Color32从 C++ 返回的对象。如果您将返回类型设置为然后在 C# 端unsigned char*使用将返回的数据复制到字节数组中,然后使用该数组将数组加载到您的. 下面是一个从 C# 返回字节数组的示例。Marshal.CopyTexture2D.LoadRawTextureDataTexture2D
我不建议您返回数据,因为这样做的成本很高。填充您传递给函数的数组,然后只需将该数组重新分配给您的Texture2D.
C++:
extern "C"
{
void ProcessImage(unsigned char* raw, int width, int height);
}
...
void ProcessImage(unsigned char* raw, int width, int height)
{
for(int y=0; y < height; y++)
{
for(int x=0; x < width; x++)
{
unsigned char* pixel = raw + (y * width * 4 + x * 4);
pixel[0] = pixel[0]-(unsigned char)2; //R
pixel[1] = pixel[1]-(unsigned char)2; //G
pixel[2] = pixel[2]-(unsigned char)2; //B
pixel[3] = pixel[3]-(unsigned char)2; //Alpha
}
}
}
Run Code Online (Sandbox Code Playgroud)
C#:
[DllImport("test22")]
private static extern void ProcessImage(IntPtr texData, int width, int height);
unsafe void ProcessImage(Texture2D texData)
{
Color32[] texDataColor = texData.GetPixels32();
System.Array.Reverse(texDataColor);
//Pin Memory
fixed (Color32* p = texDataColor)
{
ProcessImage((IntPtr)p, texData.width, texData.height);
}
//Update the Texture2D with array updated in C++
texData.SetPixels32(texDataColor);
texData.Apply();
}
Run Code Online (Sandbox Code Playgroud)
使用方法:
public Texture2D tex;
void Update()
{
ProcessImage(tex);
}
Run Code Online (Sandbox Code Playgroud)
如果您不想使用unsafe和fixed关键字,您还可以使用GCHandle.Alloc来固定数组,然后再将其发送到 C++ 端GCHandle.AddrOfPinnedObject。请参阅这篇文章了解如何执行此操作。
如果您遇到以下异常:
调用 SetPixels32 时,数组中的像素数量无效 UnityEngine.Texture2D:SetPixels32(Color32[]) Webcam:Update() (位于 Assets/Scripts/Webcam.cs:45)
这意味着Texture2D您正在调用的纹理SetPixels32具有不同的大小或纹理尺寸WebCamTexture。要解决此问题,请在调用 之前SetPixels32,检查纹理尺寸是否更改,然后调整目标纹理的大小以与WebCamTexture.
代替
tx2D.SetPixels32(rawImg);
tx2D.Apply();
Run Code Online (Sandbox Code Playgroud)
和
//Fix texture dimension if it doesn't match with the web cam texture size
if ((tx2D.width != webCamTexture.width) || tx2D.height != webCamTexture.height)
{
tx2D = new Texture2D(webCamTexture.width, webCamTexture.height, TextureFormat.RGBA32, false, false);
Debug.Log("Fixed Texture dimension");
}
tx2D.SetPixels32(rawImg);
tx2D.Apply();
Run Code Online (Sandbox Code Playgroud)