OpenGL ES
SurfaceTexture是Android端的贴图,是GL_TEXTURE_EXTERNAL_OES类型,是Android特有的类型相比普通的2D贴图有更好的性能,类似于Unity内的RenderTexture。
Unity设置
关闭多线程渲染,并且图形API选择OpenGLES2(必须)。
Unity使用SurfaceTexture
有两种方案可以实现:
一,FBO转换
已知SurfaceTexture是一个GL_TEXTURE_EXTERNAL_OES类型,在Android端使用FBO(离屏渲染)将GL_TEXTURE_EXTERNAL_OES转换到GL_TEXTURE_2D类型,Unity将GL_TEXTURE_2D转换为Texture2D再进行渲染。
二,Unity GLSL Shader
Unity 中的 GLSL - Unity 手册 (unity3d.com)
Unity中是可以直接编写OpenGL ES Shader的,将GL_TEXTURE_EXTERNAL_OES类型的贴图在Unity端直接采样渲染。
第二种方案比第一种方案更好,第二种实现:
案例-Unity打开Android摄像头并预览画面
Android代码:
private SurfaceTexture mSurfaceTexture; //camera preview
private int mTextureID;
private boolean mFrameUpdated; //帧是否更新
private Camera mCamera;
//注意处理Android权限
public void openCamera() {
mFrameUpdated = false;
mCamera = Camera.open(0);
int[] temps = new int[1];
GLES30.glGenTextures(1, temps, 0);
mTextureID = temps[0];
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
mSurfaceTexture = new SurfaceTexture(mTextureOES.getTextureID());
mSurfaceTexture.setOnFrameAvailableListener(this);
try {
mCamera.setPreviewTexture(mSurfaceTexture);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
}
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
mFrameUpdated = true;
}
public int updateTexture() {
synchronized (this) {
if (mFrameUpdated) {
mFrameUpdated = false;
}
mSurfaceTexture.updateTexImage();
return mTextureID;
}
}
public boolean isFrameUpdated() {
return mFrameUpdated;
}
public int getWidth() {
return mCamera.getParameters().getPreviewSize().width;
}
public int getHeight() {
return mCamera.getParameters().getPreviewSize().height;
}
Unity代码:
public MeshRenderer meshRenderer;
public Material material;//使用OESShader
public Texture2D texture;
int textureId;
AndroidJavaObject nativeCameraHolder;
void Awake()
{
nativeCameraHolder = new AndroidJavaObject("com.inmo.unitycore.opengles.CameraHolder");
}
void Start()
{
_openCamera();
}
void Update()
{
if (_isFrameUpdated())
{
textureId = _updateTexture();
GL.InvalidateState();
if (texture == null && textureId != 0)
{
texture = Texture2D.CreateExternalTexture(_getWidth(), _getHeight(),TextureFormat.RGB565, false, false, (IntPtr)textureId);
texture.wrapMode = TextureWrapMode.Clamp;
texture.filterMode = FilterMode.Bilinear;
}
else if (textureId != 0)
{
texture.UpdateExternalTexture((IntPtr)textureId);
meshRenderer.material.SetTexture("_MainTex", texture);
}
}
}
private void _openCamera()
{
nativeCameraHolder.Call("openCamera");
}
private void _closeCamera()
{
nativeCameraHolder.Call("closeCamera");
}
private bool _isFrameUpdated()
{
return nativeCameraHolder.Call<bool>("isFrameUpdated");
}
private int _updateTexture()
{
return nativeCameraHolder.Call<int>("updateTexture");
}
private int _getWidth()
{
return nativeCameraHolder.Call<int>("getWidth");
}
private int _getHeight()
{
return nativeCameraHolder.Call<int>("getHeight");
}
Unity Shader代码:
Shader "CF/OESShader"
{
Properties{
_MainTex("Texture", 2D) = "white" {}
_UvLeftTopBottom("UV of left corners",Vector) = (0,1,0,0)
_UvRightTopBottom("UV of right corners",Vector) = (1,1,1,0)
}
// For GLES3
SubShader
{
Pass
{
ZWrite Off
GLSLPROGRAM
#extension GL_OES_EGL_image_external : require
uniform vec4 _UvLeftTopBottom;
uniform vec4 _UvRightTopBottom;
#ifdef VERTEX
varying vec2 textureCoord;
void main()
{
vec2 uvLeft = mix(_UvLeftTopBottom.xy, _UvLeftTopBottom.zw, gl_MultiTexCoord0.y);
vec2 uvRight = mix(_UvRightTopBottom.xy, _UvRightTopBottom.zw, gl_MultiTexCoord0.y);
textureCoord = mix(uvLeft, uvRight, gl_MultiTexCoord0.x);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif
#ifdef FRAGMENT
precision mediump float; //精度为float
varying vec2 textureCoord;
uniform samplerExternalOES _MainTex;
void main()
{
gl_FragColor = texture2D(_MainTex, textureCoord);
}
#endif
ENDGLSL
}
}
FallBack Off
}