Unity插件Texture是不可变的

时间:2019-05-14 04:51:25

标签: unity3d webgl game-development

我有一个复杂的问题。在统一的WebGL中,加载纹理(使用LoadImage)会导致引擎冻结几毫秒,从而导致游戏停顿,甚至在加载大纹理时更是如此。这是一个已知的问题。

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

要执行此操作有点复杂,因此我基于WebGLMovieTexture将此解决方案作为基础,WebGLMovieTexture是资产商店中的免费资产,可让您使用播放器内置的浏览器(而不是统一的VideoPlayer)播放电影,将其转换为纹理,然后转换为gameObject。我经常使用它,而且效果很好,所以我决定尝试使用图像进行相同的操作。

为此,必须在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);
}

这是插件的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;
}   

下面的类使用上面的插件接口创建插件实例,并将URL传递给图像。然后,它会等待一小段时间以加载图像,然后调用插件的刷新功能以将纹理传输到gameObject。

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);
}

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

稍后,调用超时并调用插件的刷新功能,该功能可以将图像放置到纹理中,但是在刷新功能中,它会崩溃并显示错误

[.WebGL-0000005F0C18E320] GL_INVALID_OPERATION: Texture is immutable.

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

有人知道如何解决此错误吗?

1 个答案:

答案 0 :(得分:2)

我认为出现该错误的唯一原因是纹理被分配了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]);

对此

GLctx.texSubImage2D(GLctx.TEXTURE_2D, 0, 0, 0, GLctx.RGBA, GLctx.UNSIGNED_BYTE, imageInstances[img]);

仅供参考,尽管您的代码将由于不等待纹理实际加载而出现问题。如果用户连接缓慢,仅等待“一会儿”是不够的。您需要进行重构,以便可以在图像加载后从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);
}
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);

...

我会告诉您是否要检查图像是否已加载或使用协程检查图像是否已加载

如果您还想检查错误,则可能类似

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);
}
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);

...

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