此方法基于dx11,同時支持dx12,opengl和vulkan
1. Unity官方介面
首先要知道unity提供了C++的介面,具體位置在
unity根目錄\Editor\Data\PluginAPI\

其中標紅的就是我們需要的頭檔案,
2.官方demo參考
unity官方在檔案中介紹了相關介面的使用方法:
http://docs.unity3d.com/cn/current/Manual/NativePluginInterface.html
同時在github上提供了demo:
https://github.com/Unity-Technologies/NativeRenderingPlugin
3.代碼解讀
C#:
我假設你們是有一定的C++基礎的,在成功的運行了官方的demo之后我們可以看到實際的顯示效果是這樣的:

你可以看到在unity側的代碼沒有做什么其他操作,只是把texture和meshbuffer的指標傳給了C++

我本身不是做unity的,所以這塊各位大佬肯定比我更了解,在CallPluginAtEndOfFrames中注意一下需要用到GL.IssuePluginEvent,
C++:
我們主要來看C++這邊的代碼

可以看到在這個demo中,我主要看的是RenderAPI的子類 RenderAPI_D3D11.cpp
官方demo是生成了一個動態庫(dll),C++對texture的修改都在RenderingPlugin.cpp中,我們重點關注這段代碼:
static void ModifyTexturePixels()
{
void* textureHandle = g_TextureHandle;
int width = g_TextureWidth;
int height = g_TextureHeight;
if (!textureHandle)
return;
int textureRowPitch;
void* textureDataPtr = s_CurrentAPI->BeginModifyTexture(textureHandle, width, height, &textureRowPitch);
if (!textureDataPtr)
return;
const float t = g_Time * 4.0f;
unsigned char* dst = (unsigned char*)textureDataPtr;
for (int y = 0; y < height; ++y)
{
unsigned char* ptr = dst;
for (int x = 0; x < width; ++x)
{
// Simple "plasma effect": several combined sine waves
int vv = int(
(127.0f + (127.0f * sinf(x / 7.0f + t))) +
(127.0f + (127.0f * sinf(y / 5.0f - t))) +
(127.0f + (127.0f * sinf((x + y) / 6.0f - t))) +
(127.0f + (127.0f * sinf(sqrtf(float(x*x + y*y)) / 4.0f - t)))
) / 4;
// Write the texture pixel
ptr[0] = vv;
ptr[1] = vv;
ptr[2] = vv;
ptr[3] = vv;
// To next pixel (our pixels are 4 bpp)
ptr += 4;
}
// To next image row
dst += textureRowPitch;
}
s_CurrentAPI->EndModifyTexture(textureHandle, width, height, textureRowPitch, textureDataPtr);
}
我們可以看到根據unity傳輸過來的時間,texture中的紋理會隨著時間的變化移動,這個不是重點,我們現在想要做的是吧texture中的影像資訊傳輸到我們自己的代碼里來,
我們這里以dx11為例:實際上對texture的訪問全都在這里:

但是這里有個問題,實際上在demo中官方并沒有真正的從texture中獲取任何資料,而是自己創建了一個空的char陣列,對這個陣列進行修改,再將修改的陣列回傳給texture,這就跟我們的目的不一樣了,

因此我們需要將代碼做如下修改:
void* RenderAPI_D3D11::BeginModifyTexture(void* textureHandle, int textureWidth, int textureHeight, int* outRowPitch)
{
ID3D11Texture2D* src = (ID3D11Texture2D*)textureHandle;
D3D11_TEXTURE2D_DESC texDesc;
src->GetDesc(&texDesc);
texDesc.Usage = D3D11_USAGE_STAGING;
texDesc.BindFlags = 0;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
texDesc.MiscFlags = 0;
ID3D11DeviceContext* ctx = NULL;
m_Device->GetImmediateContext(&ctx);
ID3D11Texture2D* des = 0;
m_Device->CreateTexture2D(&texDesc, NULL, &des);
ctx->CopyResource(des, src);
D3D11_MAPPED_SUBRESOURCE mapped;
ctx->Map(des, 0, D3D11_MAP_READ, 0, &mapped);
int c = 0;
if (texDesc.Format == DXGI_FORMAT_R8_UNORM)
c = 1;
else
c = 4;
int w = mapped.RowPitch / c;
int h = mapped.DepthPitch / mapped.RowPitch;
ctx->Unmap(des, 0);
des->Release();
const int rowPitch = textureWidth * 4;
unsigned char* data = new unsigned char[rowPitch * textureHeight];
//unsigned char* data = new unsigned char[1];
memcpy(data, mapped.pData, w * h * c);
*outRowPitch = rowPitch;
return data;
}
這下我們就得到需要的資料啦!
額外提示:如果你用opencv去顯示這個資料的話,會發現影像是倒過來的,而且RGB是反的,需要自己再操作一下,這類網上很好搜的,
這個發傳輸的影像不會有gc的問題~所以速度變得更快了~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294558.html
標籤:其他
