在项目中,我通过 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;
};
函数是
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;
}
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);
获取相机情况并设置cube1、cube2 Texture
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();
}
}
通过 DllImports
将数据发送到外部 C++ 函数,并使用 Color32[] a
接收处理后的数据。最后,我使用 Unity 的 SetPixels32
设置 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();
}
结果:
结果只是立方体 1 的纹理显示实时视频,无法显示使用立方体 2 的纹理处理的数据。
错误:
SetPixels32 called with invalid number of pixels in the array UnityEngine.Texture2D:SetPixels32(Color32[]) Webcam:Update() (at Assets/Scripts/Webcam.cs:45)
我不明白为什么我将数组a输入到SetPixels32时数组中的像素数无效
有什么想法吗?
更新(2018 年 10 月 10 日)
感谢@Programmer,现在可以通过引脚内存工作。
顺便说一句,我找到了一些小的 problem这是关于 Unity 引擎的。当 Unity Camera 在 0 到 1 秒之间运行时,webCamTexture.width
或 webCamTexture.height
总是返回 16x16
大小甚至请求更大的图像,如 1280x720然后它将在 1 秒后返回正确的大小。 (Possibly several frames)所以,我引用这个 post并延迟 2 秒在 Update()
函数中运行 Process()
并在 Process()
函数中重置 Texture2D 大小。它会正常工作:
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");
}
...
}
最佳答案
C# 不返回理解您从 C++ 返回的 Color32
对象。如果您将返回类型设置为 unsigned char*
然后在 C# 端使用 Marshal.Copy
将返回的数据复制到字节数组然后使用 Texture2D.LoadRawTextureData
到将数组加载到您的 Texture2D
中。 Here 是一个从 C# 返回字节数组的示例。
我不建议您返回数据,因为这很昂贵。填充您传递给函数的数组,然后只需将该数组重新分配给您的 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
}
}
}
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();
}
使用:
public Texture2D tex;
void Update()
{
ProcessImage(tex);
}
如果你不想使用unsafe
和fixed
关键字,你也可以使用GCHandle.Alloc
在之前固定数组使用 GCHandle.AddrOfPinnedObject
将其发送到 C++ 端。有关如何执行此操作的信息,请参阅 this 帖子。
如果您遇到以下异常:
SetPixels32 called with invalid number of pixels in the array UnityEngine.Texture2D:SetPixels32(Color32[]) Webcam:Update() (at Assets/Scripts/Webcam.cs:45)
这意味着您调用 SetPixels32
的 Texture2D
与 WebCamTexture
纹理具有不同的大小或纹理维度。要解决此问题,请在调用 SetPixels32
之前检查纹理尺寸是否已更改,然后调整目标纹理的大小以匹配 WebCamTexture
。
替换
tx2D.SetPixels32(rawImg);
tx2D.Apply();
与
//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();
关于c# - 从 C++ 更新 Texture2D 像素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52686472/