Unity插件纹理是不可变的

pro*_*mer 3 game-development unity-game-engine webgl

我有一个复杂的问题。在 Unity WebGL 中,加载纹理(使用 LoadImage)会导致引擎冻结几毫秒,从而导致游戏卡顿,在加载大型纹理时情况更糟。这是一个已知问题。

为了避免冻结,我决定尝试让浏览器加载纹理,并将该纹理应用于游戏对象。这样就不会出现冻结,因为浏览器是在线程上执行的。

要做到这一点有点复杂,所以我将此解决方案基于 WebGLMovieTexture,这是资产商店中的免费资产,允许您使用内置播放器的浏览器(而不是 unity VideoPlayer)播放电影,将其应用到纹理,然后是游戏对象。我经常使用它并且它有效,所以我决定对图像尝试同样的方法。

为此,必须在 Javascript 中创建一个插件,在 C# 中创建该插件的接口类,然后创建一个使用该接口类的类。

首先是 Javascript 插件,我在这里只包含了重要的部分

var LibraryWebGLImageTexture = {

$imageInstances: [],

WebGLImageTextureCreate: function(url)
{
  var str = Pointer_stringify(url);
  var img = document.createElement('img');
  img.onload=function() {
      console.log("image load completed"); <<<-------------
  }
  img.style.display = 'none';
  img.src = str;
  return imageInstances.push(img) - 1;
},

WebGLImageTextureRefresh: function(img, tex)
{
  GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[tex]);
  GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, true);
  GLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, GLctx.RGBA,GLctx.UNSIGNED_BYTE, imageInstances[img]);
  GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, false);
}
Run Code Online (Sandbox Code Playgroud)

这是插件的 C# 接口类,同样只包含重要部分

public class WebGLImageTexture 
{
[DllImport("__Internal")]
private static extern int WebGLImageTextureCreate (string url);

[DllImport("__Internal")]
private static extern void WebGLImageTextureRefresh (int img, int texture);

public Texture2D m_Texture=null;
int m_Instance; 
bool m_Loop;

public WebGLImageTexture (string url)
{
    Debug.Log("creating image element");
    m_Instance = WebGLImageTextureCreate(url);
    imgInfo();
    Debug.Log("image element created:"+m_Instance);
}

public void imgInfo()
{
    Debug.Log("trying to get width and height...=" + m_Instance);
    var width = 672;
    var height = 420;
    m_Texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
    m_Texture.wrapMode = TextureWrapMode.Clamp;
    Debug.Log("IMAGE:"+m_Texture);
}
public void Refresh()
{
    Debug.Log("Image Update IN");
    WebGLImageTextureRefresh(m_Instance, m_Texture.GetNativeTextureID());
}

static public implicit operator Texture2D(WebGLImageTexture tex)
{
    Debug.Log("IMPLICIT TEXTURE 2D");
    return tex.m_Texture;
}   
Run Code Online (Sandbox Code Playgroud)

下面的类使用上述插件接口来创建插件实例,并将 URL 传递给图像。然后它会等待一小段时间来加载图像,然后调用插件刷新函数将纹理传输到游戏对象。

WebGLImageTexture it;   // plugin interface

void Start () {
    it = new WebGLImageTexture("http://interfacelift.com/wallpaper/previews/04194_pagview_672x420.jpg");
    gameObject.GetComponent<Renderer>().material.mainTexture = it;
    Invoke("loaded", 20); // wait for image to load then invoke this
} 


public void loaded()
{
    it.Refresh();
}

//Spin the cube

void Update () {
    transform.Rotate(new Vector3(1, 2, 3) * Time.deltaTime * 10);
}
Run Code Online (Sandbox Code Playgroud)

正如您从最顶部的 javascript 插件代码中看到的,当图像加载时,它会打印到控制台“图像加载完成”。这有效!

过了一会儿,调用超时并调用插件刷新函数,该函数会执行一些操作将图像放入纹理中,但在刷新函数中它会因错误而崩溃

[.WebGL-0000005F0C18E320] GL_INVALID_OPERATION: Texture is immutable.
Run Code Online (Sandbox Code Playgroud)

插件刷新功能似乎正在使用 OpenGL,我不知道,它在刷新功能中给出了这个错误,这是完成这项工作的关键。

有谁知道如何解决这个错误?

gma*_*man 5

我认为出现该错误的唯一原因是分配了纹理,gl.texStorage2D这意味着您只能用于gl.texSubImage2D更新纹理。

gl.texStorage2D在一次调用中分配纹理及其所有 mip 级别。从那时起,纹理的大小就无法更改。gl.texImage2D重新分配单独的 mip 级别,因此您不能使用它来更新分配的纹理gl.texStorage2D,但可以使用它更新现有纹理的内容gl.texSubImage2D

换句话说,改变这一行

GLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, GLctx.RGBA,GLctx.UNSIGNED_BYTE, imageInstances[img]);
Run Code Online (Sandbox Code Playgroud)

对此

GLctx.texSubImage2D(GLctx.TEXTURE_2D, 0, 0, 0, GLctx.RGBA, GLctx.UNSIGNED_BYTE, imageInstances[img]);
Run Code Online (Sandbox Code Playgroud)

仅供参考,尽管您的代码会因不等待纹理实际加载而出现问题。如果用户的连接速度很慢,仅仅等待“一会儿”是不够的。您需要重构,以便您可以在图像加载时从 C# 中的 JavaScript 获取事件,或者偶尔从游戏中轮询

只是猜测类似的东西

var LibraryWebGLImageTexture = {

$imageInstances: [],

WebGLImageTextureCreate: function(url)
{
  var str = Pointer_stringify(url);
  var img = new Image();
  img.src = str;
  return imageInstances.push(img) - 1;
},

WebGLImageTextureLoaded: function(img)
{
  return imageInstances[img].complete;
},

WebGLImageTextureWidth: function(img)
{
  return imageInstances[img].width;
},

WebGLImageTextureHeight: function(img)
{
  return imageInstances[img].height;
},

WebGLImageTextureRefresh: function(img, tex)
{
  GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[tex]);
  GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, true);
  GLctx.texSubImage2D(GLctx.TEXTURE_2D, 0, 0, 0, GLctx.RGBA, GLctx.UNSIGNED_BYTE, imageInstances[img]);
  GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, false);
}
Run Code Online (Sandbox Code Playgroud)
public class WebGLImageTexture 
{
[DllImport("__Internal")]
private static extern int WebGLImageTextureCreate (string url);

[DllImport("__Internal")]
private static extern bool WebGLImageTextureLoaded (int img);

[DllImport("__Internal")]
private static extern int WebGLImageTextureWidth (int img);

[DllImport("__Internal")]
private static extern int WebGLImageTextureHeight (int img);

[DllImport("__Internal")]
private static extern void WebGLImageTextureRefresh (int img, int texture);

...
Run Code Online (Sandbox Code Playgroud)

我会让您弄清楚是否要检查图像是否已加载更新或使用协程来检查图像是否已加载

如果您还想检查错误,那么也许类似

var LibraryWebGLImageTexture = {

$imageInstances: [],

WebGLImageTextureCreate: function(url)
{
  var str = Pointer_stringify(url);
  var img = new Image();
  var info = {img: img, error: false}
  img.onerror = function() {
    info.error = true;
  };
  img.src = str;
  return imageInstances.push(info) - 1;
},

WebGLImageTextureLoaded: function(img)
{
  return imageInstances[img].img.complete;
},

WebGLImageTextureError: function(img)
{
  return imageInstances[img].error;
},


WebGLImageTextureWidth: function(img)
{
  return imageInstances[img].img.width;
},

WebGLImageTextureHeight: function(img)
{
  return imageInstances[img].img.height;
},

WebGLImageTextureRefresh: function(img, tex)
{
  GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[tex]);
  GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, true);
  GLctx.texSubImage2D(GLctx.TEXTURE_2D, 0, 0, 0, GLctx.RGBA, GLctx.UNSIGNED_BYTE, imageInstances[img].img);
  GLctx.pixelStorei(GLctx.UNPACK_FLIP_Y_WEBGL, false);
}
Run Code Online (Sandbox Code Playgroud)
public class WebGLImageTexture 
{
[DllImport("__Internal")]
private static extern int WebGLImageTextureCreate (string url);

[DllImport("__Internal")]
private static extern bool WebGLImageTextureLoaded (int img);

[DllImport("__Internal")]
private static extern bool WebGLImageTextureError (int img);

[DllImport("__Internal")]
private static extern int WebGLImageTextureWidth (int img);

[DllImport("__Internal")]
private static extern int WebGLImageTextureHeight (int img);

[DllImport("__Internal")]
private static extern void WebGLImageTextureRefresh (int img, int texture);

...
Run Code Online (Sandbox Code Playgroud)

现在您可以检查您的轮询,如果WebGLImageTextureError返回 true 则表示出现错误,如果WebGLImageTextureLoaded返回 true 则图像已完成加载。