diff --git a/modules/gfx/src/CMakeLists.txt b/modules/gfx/src/CMakeLists.txt
index 8630622c35b5b03b808fce8d2e47c02015445a6e..26fb2b7b8cf1d3a339ed770e79d7bd56d6993c92 100644
--- a/modules/gfx/src/CMakeLists.txt
+++ b/modules/gfx/src/CMakeLists.txt
@@ -251,6 +251,7 @@ if (USE_SHADER)
     shader/toon_fs.glsl
     shader/toon_vs.glsl
     shader/screenblur4_fs.glsl
+    shader/test_tex_fs.glsl
   )
   copy_if_different("./" "${SHARED_DATA_PATH}/shader" "${SHADER_FILES}" 
                     "SHADER_TARGETS" ost_gfx)  
diff --git a/modules/gfx/src/impl/cartoon_renderer.cc b/modules/gfx/src/impl/cartoon_renderer.cc
index 5a10e13fa56bf79c6568ea62785947236db82f4f..a8e1bd5cdde3e398da204cd17580c53f11836548 100644
--- a/modules/gfx/src/impl/cartoon_renderer.cc
+++ b/modules/gfx/src/impl/cartoon_renderer.cc
@@ -490,15 +490,25 @@ TraceProfile CartoonRenderer::TransformAndAddProfile(const std::vector<TraceProf
                   norm[2],orth[2],dir[2]);
 
   // assemble profile with custom coloring
-  TraceProfile tf_prof(prof1.size());
+
+  /*
+    N+1 is used here because the first point
+    needs to be duplicated for texture
+    coordinate assignment to work properly
+  */
+  TraceProfile tf_prof(prof1.size()+1);
+
+
   unsigned int half=prof1.size()/2;
   unsigned int seg=prof1.size()/16;
-  for(unsigned int c=0;c<prof1.size();++c) {
-    geom::Vec3 vec=rmat*se.rad*prof1[c].v;
-    geom::Vec3 norm=rmat*prof1[c].n;
+  for(unsigned int c=0;c<prof1.size()+1;++c) {
+    // use cc everywhere except for the texture coordinate calculation
+    int cc=c%prof1.size();
+    geom::Vec3 vec=rmat*se.rad*prof1[cc].v;
+    geom::Vec3 norm=rmat*prof1[cc].n;
     if(fuse_flag) {
-      geom::Vec3 vec2=rmat*se.rad*prof2[c].v;
-      geom::Vec3 norm2=rmat*prof2[c].n;
+      geom::Vec3 vec2=rmat*se.rad*prof2[cc].v;
+      geom::Vec3 norm2=rmat*prof2[cc].n;
       vec=se.position+(1.0f-se.frac)*vec+se.frac*vec2;
       norm=Normalize((1.0f-se.frac)*norm+se.frac*norm2);
     } else {
@@ -516,7 +526,10 @@ TraceProfile CartoonRenderer::TransformAndAddProfile(const std::vector<TraceProf
     } else if(se.type==2 || se.type==3) {
       if(c<=seg || (c>=half-seg && c<=half+seg) || c>=prof1.size()-seg) col=se.color2;
     }
-    tf_prof[c].id=va.Add(vec,norm, col);
+    // c is used instead of cc to get 1.0 for the last point
+    float tx=static_cast<float>(c)/static_cast<float>(prof1.size());
+    float ty=se.running_length;
+    tf_prof[c].id=va.Add(vec,norm,col,geom::Vec2(tx,ty));
   }
   return tf_prof;
 }
@@ -535,15 +548,26 @@ void CartoonRenderer::AssembleProfile(const TraceProfile& prof1,
                                       const TraceProfile& prof2, 
                                       IndexedVertexArray& va)
 {
+  /*
+    the wrap around algorithm used here needs to take into account
+    that the TraceProfile has a duplicate entry for prof*[0] and
+    prof*[N-1], which fall onto the same point except and have
+    the same normal but a different texture coordinate. Hence
+    all the mods are done with size()-1, but in the assembly
+    routine prof*[0] is turned into prof*[N-1] if necessary
+  */
+  size_t size=prof1.size()-1;
+
+  // first get the best correction offset
   float accum[]={0.0,0.0,0.0,0.0,0.0};
-  for(int i=0;i<prof1.size();++i) {
-    int i1=(i+0)%prof1.size();
-    int i2=(i+1)%prof1.size();
+  for(int i=0;i<size;++i) {
+    int i1=(i+0)%(size);
+    int i2=(i+1)%(size);
     geom::Vec3 v1=va.GetVert(prof1[i1].id);
     geom::Vec3 v2=va.GetVert(prof1[i2].id);
     for(int k=-2;k<=2;++k) {
-      int i3=(i+k+0+prof1.size())%prof1.size();
-      int i4=(i+k+1+prof1.size())%prof1.size();
+      int i3=(i+k+0+size)%(size);
+      int i4=(i+k+1+size)%(size);
       geom::Vec3 v3=va.GetVert(prof2[i3].id);
       geom::Vec3 v4=va.GetVert(prof2[i4].id);
       accum[k+2]+=spread(v1,v2,v3,v4);
@@ -558,13 +582,17 @@ void CartoonRenderer::AssembleProfile(const TraceProfile& prof1,
       best_off=k;
     }
   }
-  best_off=(best_off+prof1.size())%prof1.size();
+  best_off=(best_off+(size))%(size);
+
+  // now assemble the triangles
+  for(unsigned int i1=0;i1<size;++i1) {
+    unsigned int i2=(i1+1)%(size);
+    unsigned int i3=(i1+best_off)%(size);
+    unsigned int i4=(i1+best_off+1)%(size);
 
-  // assume both profiles have the same size
-  for(unsigned int i1=0;i1<prof1.size();++i1) {
-    unsigned int i2=(i1+1)%prof1.size();
-    unsigned int i3=(i1+best_off)%prof1.size();
-    unsigned int i4=(i1+best_off+1)%prof1.size();
+    // wrap around correction for proper texture coordinates
+    i2 = (i2==0) ? size : i2;
+    i4 = (i4==0) ? size : i4;
 
 #if 1
     va.AddTri(prof1[i1].id,prof1[i2].id,prof2[i3].id);
@@ -580,13 +608,16 @@ void CartoonRenderer::CapProfile(const impl::TraceProfile& p,
                                  bool flipn, IndexedVertexArray& va)
 {
   geom::Vec3 norm=flipn ? -se.direction : se.direction;
-  VertexID pi0 = va.Add(se.position,norm, se.color1);
+  VertexID pi0 = va.Add(se.position,norm, se.color1,geom::Vec2(0.5,0.5));
   std::vector<VertexID> vertices(p.size());
+  float fac=2.0*M_PI/static_cast<float>(p.size()-1);
   for(unsigned int i=0;i<p.size();++i) {
-    vertices[i]=va.Add(p[i].v,norm,se.color1);
+    float aa=fac*static_cast<float>(i%(p.size()-1));
+    vertices[i]=va.Add(p[i].v,norm,se.color1,geom::Vec2(0.5*cos(aa)+0.5,0.5*sin(aa)+0.5));
   }
-  for(unsigned int i1=0;i1<p.size();++i1) {
-    unsigned int i2=(i1+1)%p.size();
+  // taking first/last duplication into account again (see AssembleProfile)
+  for(unsigned int i1=0;i1<p.size()-1;++i1) {
+    unsigned int i2=i1+1;
     if(flipn) {
       va.AddTri(pi0,vertices[i2],vertices[i1]);
     } else {
diff --git a/modules/gfx/src/impl/entity_detail.cc b/modules/gfx/src/impl/entity_detail.cc
index 59f1042337de0328a45bbc98a35fc3a75bb02445..fb78ff377b0605ba6659f2a9b0703cc095852fcd 100644
--- a/modules/gfx/src/impl/entity_detail.cc
+++ b/modules/gfx/src/impl/entity_detail.cc
@@ -378,9 +378,10 @@ SplineEntryList Spline::Generate(const SplineEntryList& entry_list, int nsub, ui
       sublist.at(c*nsub+d).type=entry_list[c].type;
       sublist.at(c*nsub+d).type1=type1;
       sublist.at(c*nsub+d).type2=type2;
-      sublist.at(c*nsub+d).frac=float(d)/float(nsub);
+      float frac=float(d)/float(nsub);
+      sublist.at(c*nsub+d).frac=frac;
     }
-  }                                                   
+  }
   int type1=entry_list.back().type;
   int type2=type1;
   sublist.back().type=entry_list.back().type;
@@ -388,6 +389,11 @@ SplineEntryList Spline::Generate(const SplineEntryList& entry_list, int nsub, ui
   sublist.back().type2=type2;
   sublist.back().frac=0.0;
 
+  float insub=1.0/static_cast<float>(nsub);
+  for(int c=0;c<sublist.size();++c) {
+    sublist[c].running_length=static_cast<float>(c)*insub;
+  }
+
   // the id for selections, shifted by one half
   for(int c=0;c<size-1;++c) {
     int d=0;
diff --git a/modules/gfx/src/impl/entity_detail.hh b/modules/gfx/src/impl/entity_detail.hh
index 243ade1572723e578753901f6c9bb0ffdfe0f0f3..2ae0a848f14c3e7c711f67dc31e436969a4a22d0 100644
--- a/modules/gfx/src/impl/entity_detail.hh
+++ b/modules/gfx/src/impl/entity_detail.hh
@@ -123,6 +123,7 @@ struct DLLEXPORT_OST_GFX SplineEntry {
     type1(0),
     type2(0),
     frac(0.0),
+    running_length(0.0),
     v0(1.0,0.0,0.0),
     v1(0.0,1.0,0.0),
     v2(0.0,0.0,1.0),
@@ -137,7 +138,7 @@ struct DLLEXPORT_OST_GFX SplineEntry {
               const Color& c1, const Color& c2,
               unsigned int t, int i):
     position(p),direction(d),normal(n),color1(c1),color2(c2),rad(r),type(t),
-    type1(t),type2(t),frac(0.0),v0(),v1(),v2(),nflip(false),id(i)
+    type1(t),type2(t),frac(0.0),running_length(0.0),v0(),v1(),v2(),nflip(false),id(i)
   {
   }
 
@@ -146,7 +147,7 @@ struct DLLEXPORT_OST_GFX SplineEntry {
   float rad;
   unsigned int type;
   unsigned int type1, type2;
-  float frac;
+  float frac,running_length;
   geom::Vec3 v0,v1,v2; // helper vectors
   bool nflip;
   int id;
diff --git a/modules/gfx/src/scene.cc b/modules/gfx/src/scene.cc
index fdac2ef582bc0d7ccd7e43f68b09b8d6cac48e3e..b2fbd79d54eb53c6dfefdf80fc8f0942fcac5938 100644
--- a/modules/gfx/src/scene.cc
+++ b/modules/gfx/src/scene.cc
@@ -276,6 +276,8 @@ void Scene::SetShadingMode(const std::string& smode)
     Shader::Instance().Activate("toon1");
   } else if(smode=="toon2") {
     Shader::Instance().Activate("toon2");
+  } else if(smode=="test_tex") {
+    Shader::Instance().Activate("test_tex");
   } else {
     Shader::Instance().Activate("fraglight");
   }
diff --git a/modules/gfx/src/shader.cc b/modules/gfx/src/shader.cc
index 81df738587895a1f4c8dad546a78bcea749c441a..b82bae72b21fc20766fa869f79cc2df5b31783d5 100644
--- a/modules/gfx/src/shader.cc
+++ b/modules/gfx/src/shader.cc
@@ -162,7 +162,8 @@ void Shader::Setup()
     {"scenefx_vs.glsl", GL_VERTEX_SHADER},
     {"scenefx_fs.glsl", GL_FRAGMENT_SHADER},
     {"beacon_fs.glsl", GL_FRAGMENT_SHADER},
-    {"screenblur4_fs.glsl", GL_FRAGMENT_SHADER}
+    {"screenblur4_fs.glsl", GL_FRAGMENT_SHADER},
+    {"test_tex_fs.glsl", GL_FRAGMENT_SHADER}
     //////////////////////////////////////////////////////////////////
   };
 
@@ -305,6 +306,13 @@ void Shader::Setup()
   if(link_shader(shader_program_list,"screenblur4",shader_program_id)) {
     shader_program_map_["screenblur4"]=shader_program_id;
   }
+  // test tex shader
+  shader_program_list.clear();
+  shader_program_list.push_back(shader_code_map_["fraglight_vs.glsl"]);
+  shader_program_list.push_back(shader_code_map_["test_tex_fs.glsl"]);
+  if(link_shader(shader_program_list,"test_tex",shader_program_id)) {
+    shader_program_map_["test_tex"]=shader_program_id;
+  }
 
   valid_=true;
 }
diff --git a/modules/gfx/src/shader/test_tex_fs.glsl b/modules/gfx/src/shader/test_tex_fs.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..575f75dfda4409b0267f9674d23fbcabc119e10b
--- /dev/null
+++ b/modules/gfx/src/shader/test_tex_fs.glsl
@@ -0,0 +1,6 @@
+void main()
+{
+  gl_FragColor.rg=frac(gl_TexCoord[0].st);
+  gl_FragColor.b=1.0;
+  gl_FragColor.a=1.0;
+}
diff --git a/modules/gfx/src/vertex_array.cc b/modules/gfx/src/vertex_array.cc
index 541b881511c621c4bf3aa3df75d91ed319059231..b47a682a9a8be373d41d61d3a9acf61af5fc12b1 100644
--- a/modules/gfx/src/vertex_array.cc
+++ b/modules/gfx/src/vertex_array.cc
@@ -670,7 +670,7 @@ void IndexedVertexArray::Reset()
   outline_exp_factor_=0.1;
   outline_exp_color_=Color(0,0,0);
   draw_normals_=false;
-  use_tex_=false;
+  use_tex_=true;
 }
 
 void IndexedVertexArray::FlagRefresh()
@@ -1146,7 +1146,7 @@ void IndexedVertexArray::draw_ltq(bool use_buff)
 {
   if(use_buff && !Scene::Instance().InOffscreenMode()) {
 #if OST_SHADER_SUPPORT_ENABLED
-#if 1
+#if 0
     /*
       for now, since v,n,c live in a packed format (formerly used
       with glInterleavedArrays), only a single buffer is
diff --git a/modules/gfx/src/vertex_array.hh b/modules/gfx/src/vertex_array.hh
index 332f03df3eadf12f2414585c02c070ddb43ba1b5..3883c2abe9e298be14c9f64a7d091d51b52e4bda 100644
--- a/modules/gfx/src/vertex_array.hh
+++ b/modules/gfx/src/vertex_array.hh
@@ -111,7 +111,7 @@ class DLLEXPORT_OST_GFX IndexedVertexArray {
   void SetOutlineExpandFactor(float f);
   void SetOutlineExpandColor(const Color& c);
 
-  // vertex, normal, and color (C4F_N3F_V3F)
+  // vertex, normal, color and texcoord (T2F_C4F_N3F_V3F)
   VertexID Add(const geom::Vec3& vert, const geom::Vec3& norm, const Color& col, const geom::Vec2& tex=geom::Vec2());
 
   unsigned int GetVertexCount() const;
@@ -182,7 +182,7 @@ class DLLEXPORT_OST_GFX IndexedVertexArray {
   // experimental, do not use
   void SmoothVertices(float smoothf);
 
-  void SetTex(bool b) {use_tex_=b;}
+  void UseTex(bool b) {use_tex_=b;}
   uint& TexID() {return tex_id_;}
 
   const EntryList& GetEntries() const {return entry_list_;}